Coverage Report

Created: 2026-05-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/MagickCore/display.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7
%               D   D    I    SS     P   P  L      A   A   Y Y                %
8
%               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9
%               D   D    I       SS  P      L      A   A    Y                 %
10
%               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11
%                                                                             %
12
%                                                                             %
13
%        MagickCore Methods to Interactively Display and Edit an Image        %
14
%                                                                             %
15
%                             Software Design                                 %
16
%                                  Cristy                                     %
17
%                                July 1992                                    %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/license/                                         %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
*/
38

39
/*
40
  Include declarations.
41
*/
42
#include "MagickCore/studio.h"
43
#include "MagickCore/artifact.h"
44
#include "MagickCore/attribute.h"
45
#include "MagickCore/blob.h"
46
#include "MagickCore/cache.h"
47
#include "MagickCore/cache-private.h"
48
#include "MagickCore/channel.h"
49
#include "MagickCore/client.h"
50
#include "MagickCore/color.h"
51
#include "MagickCore/colorspace.h"
52
#include "MagickCore/composite.h"
53
#include "MagickCore/constitute.h"
54
#include "MagickCore/decorate.h"
55
#include "MagickCore/delegate.h"
56
#include "MagickCore/display.h"
57
#include "MagickCore/display-private.h"
58
#include "MagickCore/distort.h"
59
#include "MagickCore/draw.h"
60
#include "MagickCore/effect.h"
61
#include "MagickCore/enhance.h"
62
#include "MagickCore/exception.h"
63
#include "MagickCore/exception-private.h"
64
#include "MagickCore/fx.h"
65
#include "MagickCore/geometry.h"
66
#include "MagickCore/image.h"
67
#include "MagickCore/image-private.h"
68
#include "MagickCore/list.h"
69
#include "MagickCore/locale-private.h"
70
#include "MagickCore/log.h"
71
#include "MagickCore/magick.h"
72
#include "MagickCore/memory_.h"
73
#include "MagickCore/monitor.h"
74
#include "MagickCore/monitor-private.h"
75
#include "MagickCore/montage.h"
76
#include "MagickCore/nt-base-private.h"
77
#include "MagickCore/option.h"
78
#include "MagickCore/paint.h"
79
#include "MagickCore/pixel.h"
80
#include "MagickCore/pixel-accessor.h"
81
#include "MagickCore/property.h"
82
#include "MagickCore/quantum.h"
83
#include "MagickCore/quantum-private.h"
84
#include "MagickCore/resize.h"
85
#include "MagickCore/resource_.h"
86
#include "MagickCore/shear.h"
87
#include "MagickCore/segment.h"
88
#include "MagickCore/statistic.h"
89
#include "MagickCore/string_.h"
90
#include "MagickCore/string-private.h"
91
#include "MagickCore/timer-private.h"
92
#include "MagickCore/transform.h"
93
#include "MagickCore/transform-private.h"
94
#include "MagickCore/threshold.h"
95
#include "MagickCore/utility.h"
96
#include "MagickCore/utility-private.h"
97
#include "MagickCore/version.h"
98
#include "MagickCore/visual-effects.h"
99
#include "MagickCore/widget.h"
100
#include "MagickCore/widget-private.h"
101
#include "MagickCore/xwindow.h"
102
#include "MagickCore/xwindow-private.h"
103

104
#if defined(MAGICKCORE_X11_DELEGATE)
105
/*
106
  Define declarations.
107
*/
108
#define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
109

110
/*
111
  Constant declarations.
112
*/
113
static const unsigned char
114
  HighlightBitmap[8] =
115
  {
116
    0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
117
  },
118
  OpaqueBitmap[8] =
119
  {
120
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
121
  },
122
  ShadowBitmap[8] =
123
  {
124
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
125
  };
126

127
/*
128
  Help widget declarations.
129
*/
130
static const char
131
  ImageAnnotateHelp[] =
132
  {
133
    "In annotate mode, the Command widget has these options:\n"
134
    "\n"
135
    "    Font Name\n"
136
    "      fixed\n"
137
    "      variable\n"
138
    "      5x8\n"
139
    "      6x10\n"
140
    "      7x13bold\n"
141
    "      8x13bold\n"
142
    "      9x15bold\n"
143
    "      10x20\n"
144
    "      12x24\n"
145
    "      Browser...\n"
146
    "    Font Color\n"
147
    "      black\n"
148
    "      blue\n"
149
    "      cyan\n"
150
    "      green\n"
151
    "      gray\n"
152
    "      red\n"
153
    "      magenta\n"
154
    "      yellow\n"
155
    "      white\n"
156
    "      transparent\n"
157
    "      Browser...\n"
158
    "    Font Color\n"
159
    "      black\n"
160
    "      blue\n"
161
    "      cyan\n"
162
    "      green\n"
163
    "      gray\n"
164
    "      red\n"
165
    "      magenta\n"
166
    "      yellow\n"
167
    "      white\n"
168
    "      transparent\n"
169
    "      Browser...\n"
170
    "    Rotate Text\n"
171
    "      -90\n"
172
    "      -45\n"
173
    "      -30\n"
174
    "      0\n"
175
    "      30\n"
176
    "      45\n"
177
    "      90\n"
178
    "      180\n"
179
    "      Dialog...\n"
180
    "    Help\n"
181
    "    Dismiss\n"
182
    "\n"
183
    "Choose a font name from the Font Name sub-menu.  Additional\n"
184
    "font names can be specified with the font browser.  You can\n"
185
    "change the menu names by setting the X resources font1\n"
186
    "through font9.\n"
187
    "\n"
188
    "Choose a font color from the Font Color sub-menu.\n"
189
    "Additional font colors can be specified with the color\n"
190
    "browser.  You can change the menu colors by setting the X\n"
191
    "resources pen1 through pen9.\n"
192
    "\n"
193
    "If you select the color browser and press Grab, you can\n"
194
    "choose the font color by moving the pointer to the desired\n"
195
    "color on the screen and press any button.\n"
196
    "\n"
197
    "If you choose to rotate the text, choose Rotate Text from the\n"
198
    "menu and select an angle.  Typically you will only want to\n"
199
    "rotate one line of text at a time.  Depending on the angle you\n"
200
    "choose, subsequent lines may end up overwriting each other.\n"
201
    "\n"
202
    "Choosing a font and its color is optional.  The default font\n"
203
    "is fixed and the default color is black.  However, you must\n"
204
    "choose a location to begin entering text and press button 1.\n"
205
    "An underscore character will appear at the location of the\n"
206
    "pointer.  The cursor changes to a pencil to indicate you are\n"
207
    "in text mode.  To exit immediately, press Dismiss.\n"
208
    "\n"
209
    "In text mode, any key presses will display the character at\n"
210
    "the location of the underscore and advance the underscore\n"
211
    "cursor.  Enter your text and once completed press Apply to\n"
212
    "finish your image annotation.  To correct errors press BACK\n"
213
    "SPACE.  To delete an entire line of text, press DELETE.  Any\n"
214
    "text that exceeds the boundaries of the image window is\n"
215
    "automagically continued onto the next line.\n"
216
    "\n"
217
    "The actual color you request for the font is saved in the\n"
218
    "image.  However, the color that appears in your image window\n"
219
    "may be different.  For example, on a monochrome screen the\n"
220
    "text will appear black or white even if you choose the color\n"
221
    "red as the font color.  However, the image saved to a file\n"
222
    "with -write is written with red lettering.  To assure the\n"
223
    "correct color text in the final image, any PseudoClass image\n"
224
    "is promoted to DirectClass (see miff(5)).  To force a\n"
225
    "PseudoClass image to remain PseudoClass, use -colors.\n"
226
  },
227
  ImageChopHelp[] =
228
  {
229
    "In chop mode, the Command widget has these options:\n"
230
    "\n"
231
    "    Direction\n"
232
    "      horizontal\n"
233
    "      vertical\n"
234
    "    Help\n"
235
    "    Dismiss\n"
236
    "\n"
237
    "If the you choose the horizontal direction (this the\n"
238
    "default), the area of the image between the two horizontal\n"
239
    "endpoints of the chop line is removed.  Otherwise, the area\n"
240
    "of the image between the two vertical endpoints of the chop\n"
241
    "line is removed.\n"
242
    "\n"
243
    "Select a location within the image window to begin your chop,\n"
244
    "press and hold any button.  Next, move the pointer to\n"
245
    "another location in the image.  As you move a line will\n"
246
    "connect the initial location and the pointer.  When you\n"
247
    "release the button, the area within the image to chop is\n"
248
    "determined by which direction you choose from the Command\n"
249
    "widget.\n"
250
    "\n"
251
    "To cancel the image chopping, move the pointer back to the\n"
252
    "starting point of the line and release the button.\n"
253
  },
254
  ImageColorEditHelp[] =
255
  {
256
    "In color edit mode, the Command widget has these options:\n"
257
    "\n"
258
    "    Method\n"
259
    "      point\n"
260
    "      replace\n"
261
    "      floodfill\n"
262
    "      filltoborder\n"
263
    "      reset\n"
264
    "    Pixel Color\n"
265
    "      black\n"
266
    "      blue\n"
267
    "      cyan\n"
268
    "      green\n"
269
    "      gray\n"
270
    "      red\n"
271
    "      magenta\n"
272
    "      yellow\n"
273
    "      white\n"
274
    "      Browser...\n"
275
    "    Border Color\n"
276
    "      black\n"
277
    "      blue\n"
278
    "      cyan\n"
279
    "      green\n"
280
    "      gray\n"
281
    "      red\n"
282
    "      magenta\n"
283
    "      yellow\n"
284
    "      white\n"
285
    "      Browser...\n"
286
    "    Fuzz\n"
287
    "      0%\n"
288
    "      2%\n"
289
    "      5%\n"
290
    "      10%\n"
291
    "      15%\n"
292
    "      Dialog...\n"
293
    "    Undo\n"
294
    "    Help\n"
295
    "    Dismiss\n"
296
    "\n"
297
    "Choose a color editing method from the Method sub-menu\n"
298
    "of the Command widget.  The point method recolors any pixel\n"
299
    "selected with the pointer until the button is released.  The\n"
300
    "replace method recolors any pixel that matches the color of\n"
301
    "the pixel you select with a button press.  Floodfill recolors\n"
302
    "any pixel that matches the color of the pixel you select with\n"
303
    "a button press and is a neighbor.  Whereas filltoborder recolors\n"
304
    "any neighbor pixel that is not the border color.  Finally reset\n"
305
    "changes the entire image to the designated color.\n"
306
    "\n"
307
    "Next, choose a pixel color from the Pixel Color sub-menu.\n"
308
    "Additional pixel colors can be specified with the color\n"
309
    "browser.  You can change the menu colors by setting the X\n"
310
    "resources pen1 through pen9.\n"
311
    "\n"
312
    "Now press button 1 to select a pixel within the image window\n"
313
    "to change its color.  Additional pixels may be recolored as\n"
314
    "prescribed by the method you choose.\n"
315
    "\n"
316
    "If the Magnify widget is mapped, it can be helpful in positioning\n"
317
    "your pointer within the image (refer to button 2).\n"
318
    "\n"
319
    "The actual color you request for the pixels is saved in the\n"
320
    "image.  However, the color that appears in your image window\n"
321
    "may be different.  For example, on a monochrome screen the\n"
322
    "pixel will appear black or white even if you choose the\n"
323
    "color red as the pixel color.  However, the image saved to a\n"
324
    "file with -write is written with red pixels.  To assure the\n"
325
    "correct color text in the final image, any PseudoClass image\n"
326
    "is promoted to DirectClass (see miff(5)).  To force a\n"
327
    "PseudoClass image to remain PseudoClass, use -colors.\n"
328
  },
329
  ImageCompositeHelp[] =
330
  {
331
    "First a widget window is displayed requesting you to enter an\n"
332
    "image name. Press Composite, Grab or type a file name.\n"
333
    "Press Cancel if you choose not to create a composite image.\n"
334
    "When you choose Grab, move the pointer to the desired window\n"
335
    "and press any button.\n"
336
    "\n"
337
    "If the Composite image does not have any matte information,\n"
338
    "you are informed and the file browser is displayed again.\n"
339
    "Enter the name of a mask image.  The image is typically\n"
340
    "grayscale and the same size as the composite image.  If the\n"
341
    "image is not grayscale, it is converted to grayscale and the\n"
342
    "resulting intensities are used as matte information.\n"
343
    "\n"
344
    "A small window appears showing the location of the cursor in\n"
345
    "the image window. You are now in composite mode.  To exit\n"
346
    "immediately, press Dismiss.  In composite mode, the Command\n"
347
    "widget has these options:\n"
348
    "\n"
349
    "    Operators\n"
350
    "      Over\n"
351
    "      In\n"
352
    "      Out\n"
353
    "      Atop\n"
354
    "      Xor\n"
355
    "      Plus\n"
356
    "      Minus\n"
357
    "      Add\n"
358
    "      Subtract\n"
359
    "      Difference\n"
360
    "      Multiply\n"
361
    "      Bumpmap\n"
362
    "      Copy\n"
363
    "      CopyRed\n"
364
    "      CopyGreen\n"
365
    "      CopyBlue\n"
366
    "      CopyOpacity\n"
367
    "      Clear\n"
368
    "    Dissolve\n"
369
    "    Displace\n"
370
    "    Help\n"
371
    "    Dismiss\n"
372
    "\n"
373
    "Choose a composite operation from the Operators sub-menu of\n"
374
    "the Command widget.  How each operator behaves is described\n"
375
    "below.  Image window is the image currently displayed on\n"
376
    "your X server and image is the image obtained with the File\n"
377
    "Browser widget.\n"
378
    "\n"
379
    "Over     The result is the union of the two image shapes,\n"
380
    "         with image obscuring image window in the region of\n"
381
    "         overlap.\n"
382
    "\n"
383
    "In       The result is simply image cut by the shape of\n"
384
    "         image window.  None of the image data of image\n"
385
    "         window is in the result.\n"
386
    "\n"
387
    "Out      The resulting image is image with the shape of\n"
388
    "         image window cut out.\n"
389
    "\n"
390
    "Atop     The result is the same shape as the image window,\n"
391
    "         with image obscuring image window where the image\n"
392
    "         shapes overlap.  Note this differs from over\n"
393
    "         because the portion of image outside image window's\n"
394
    "         shape does not appear in the result.\n"
395
    "\n"
396
    "Xor      The result is the image data from both image and\n"
397
    "         image window that is outside the overlap region.\n"
398
    "         The overlap region is blank.\n"
399
    "\n"
400
    "Plus     The result is just the sum of the image data.\n"
401
    "         Output values are cropped to QuantumRange (no overflow).\n"
402
    "\n"
403
    "Minus    The result of image - image window, with underflow\n"
404
    "         cropped to zero.\n"
405
    "\n"
406
    "Add      The result of image + image window, with overflow\n"
407
    "         wrapping around (mod 256).\n"
408
    "\n"
409
    "Subtract The result of image - image window, with underflow\n"
410
    "         wrapping around (mod 256).  The add and subtract\n"
411
    "         operators can be used to perform reversible\n"
412
    "         transformations.\n"
413
    "\n"
414
    "Difference\n"
415
    "         The result of abs(image - image window).  This\n"
416
    "         useful for comparing two very similar images.\n"
417
    "\n"
418
    "Multiply\n"
419
    "         The result of image * image window.  This\n"
420
    "         useful for the creation of drop-shadows.\n"
421
    "\n"
422
    "Bumpmap  The result of surface normals from image * image\n"
423
    "         window.\n"
424
    "\n"
425
    "Copy     The resulting image is image window replaced with\n"
426
    "         image.  Here the matte information is ignored.\n"
427
    "\n"
428
    "CopyRed  The red layer of the image window is replace with\n"
429
    "         the red layer of the image.  The other layers are\n"
430
    "         untouched.\n"
431
    "\n"
432
    "CopyGreen\n"
433
    "         The green layer of the image window is replace with\n"
434
    "         the green layer of the image.  The other layers are\n"
435
    "         untouched.\n"
436
    "\n"
437
    "CopyBlue The blue layer of the image window is replace with\n"
438
    "         the blue layer of the image.  The other layers are\n"
439
    "         untouched.\n"
440
    "\n"
441
    "CopyOpacity\n"
442
    "         The matte layer of the image window is replace with\n"
443
    "         the matte layer of the image.  The other layers are\n"
444
    "         untouched.\n"
445
    "\n"
446
    "The image compositor requires a matte, or alpha channel in\n"
447
    "the image for some operations.  This extra channel usually\n"
448
    "defines a mask which represents a sort of a cookie-cutter\n"
449
    "for the image.  This the case when matte is opaque (full\n"
450
    "coverage) for pixels inside the shape, zero outside, and\n"
451
    "between 0 and QuantumRange on the boundary.  If image does not\n"
452
    "have a matte channel, it is initialized with 0 for any pixel\n"
453
    "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
454
    "\n"
455
    "If you choose Dissolve, the composite operator becomes Over.  The\n"
456
    "image matte channel percent transparency is initialized to factor.\n"
457
    "The image window is initialized to (100-factor). Where factor is the\n"
458
    "value you specify in the Dialog widget.\n"
459
    "\n"
460
    "Displace shifts the image pixels as defined by a displacement\n"
461
    "map.  With this option, image is used as a displacement map.\n"
462
    "Black, within the displacement map, is a maximum positive\n"
463
    "displacement.  White is a maximum negative displacement and\n"
464
    "middle gray is neutral.  The displacement is scaled to determine\n"
465
    "the pixel shift.  By default, the displacement applies in both the\n"
466
    "horizontal and vertical directions.  However, if you specify a mask,\n"
467
    "image is the horizontal X displacement and mask the vertical Y\n"
468
    "displacement.\n"
469
    "\n"
470
    "Note that matte information for image window is not retained\n"
471
    "for colormapped X server visuals (e.g. StaticColor,\n"
472
    "StaticColor, GrayScale, PseudoColor).  Correct compositing\n"
473
    "behavior may require a TrueColor or DirectColor visual or a\n"
474
    "Standard Colormap.\n"
475
    "\n"
476
    "Choosing a composite operator is optional.  The default\n"
477
    "operator is replace.  However, you must choose a location to\n"
478
    "composite your image and press button 1.  Press and hold the\n"
479
    "button before releasing and an outline of the image will\n"
480
    "appear to help you identify your location.\n"
481
    "\n"
482
    "The actual colors of the composite image is saved.  However,\n"
483
    "the color that appears in image window may be different.\n"
484
    "For example, on a monochrome screen image window will appear\n"
485
    "black or white even though your composited image may have\n"
486
    "many colors.  If the image is saved to a file it is written\n"
487
    "with the correct colors.  To assure the correct colors are\n"
488
    "saved in the final image, any PseudoClass image is promoted\n"
489
    "to DirectClass (see miff(5)).  To force a PseudoClass image\n"
490
    "to remain PseudoClass, use -colors.\n"
491
  },
492
  ImageCutHelp[] =
493
  {
494
    "In cut mode, the Command widget has these options:\n"
495
    "\n"
496
    "    Help\n"
497
    "    Dismiss\n"
498
    "\n"
499
    "To define a cut region, press button 1 and drag.  The\n"
500
    "cut region is defined by a highlighted rectangle that\n"
501
    "expands or contracts as it follows the pointer.  Once you\n"
502
    "are satisfied with the cut region, release the button.\n"
503
    "You are now in rectify mode.  In rectify mode, the Command\n"
504
    "widget has these options:\n"
505
    "\n"
506
    "    Cut\n"
507
    "    Help\n"
508
    "    Dismiss\n"
509
    "\n"
510
    "You can make adjustments by moving the pointer to one of the\n"
511
    "cut rectangle corners, pressing a button, and dragging.\n"
512
    "Finally, press Cut to commit your copy region.  To\n"
513
    "exit without cutting the image, press Dismiss.\n"
514
  },
515
  ImageCopyHelp[] =
516
  {
517
    "In copy mode, the Command widget has these options:\n"
518
    "\n"
519
    "    Help\n"
520
    "    Dismiss\n"
521
    "\n"
522
    "To define a copy region, press button 1 and drag.  The\n"
523
    "copy region is defined by a highlighted rectangle that\n"
524
    "expands or contracts as it follows the pointer.  Once you\n"
525
    "are satisfied with the copy region, release the button.\n"
526
    "You are now in rectify mode.  In rectify mode, the Command\n"
527
    "widget has these options:\n"
528
    "\n"
529
    "    Copy\n"
530
    "    Help\n"
531
    "    Dismiss\n"
532
    "\n"
533
    "You can make adjustments by moving the pointer to one of the\n"
534
    "copy rectangle corners, pressing a button, and dragging.\n"
535
    "Finally, press Copy to commit your copy region.  To\n"
536
    "exit without copying the image, press Dismiss.\n"
537
  },
538
  ImageCropHelp[] =
539
  {
540
    "In crop mode, the Command widget has these options:\n"
541
    "\n"
542
    "    Help\n"
543
    "    Dismiss\n"
544
    "\n"
545
    "To define a cropping region, press button 1 and drag.  The\n"
546
    "cropping region is defined by a highlighted rectangle that\n"
547
    "expands or contracts as it follows the pointer.  Once you\n"
548
    "are satisfied with the cropping region, release the button.\n"
549
    "You are now in rectify mode.  In rectify mode, the Command\n"
550
    "widget has these options:\n"
551
    "\n"
552
    "    Crop\n"
553
    "    Help\n"
554
    "    Dismiss\n"
555
    "\n"
556
    "You can make adjustments by moving the pointer to one of the\n"
557
    "cropping rectangle corners, pressing a button, and dragging.\n"
558
    "Finally, press Crop to commit your cropping region.  To\n"
559
    "exit without cropping the image, press Dismiss.\n"
560
  },
561
  ImageDrawHelp[] =
562
  {
563
    "The cursor changes to a crosshair to indicate you are in\n"
564
    "draw mode.  To exit immediately, press Dismiss.  In draw mode,\n"
565
    "the Command widget has these options:\n"
566
    "\n"
567
    "    Element\n"
568
    "      point\n"
569
    "      line\n"
570
    "      rectangle\n"
571
    "      fill rectangle\n"
572
    "      circle\n"
573
    "      fill circle\n"
574
    "      ellipse\n"
575
    "      fill ellipse\n"
576
    "      polygon\n"
577
    "      fill polygon\n"
578
    "    Color\n"
579
    "      black\n"
580
    "      blue\n"
581
    "      cyan\n"
582
    "      green\n"
583
    "      gray\n"
584
    "      red\n"
585
    "      magenta\n"
586
    "      yellow\n"
587
    "      white\n"
588
    "      transparent\n"
589
    "      Browser...\n"
590
    "    Stipple\n"
591
    "      Brick\n"
592
    "      Diagonal\n"
593
    "      Scales\n"
594
    "      Vertical\n"
595
    "      Wavy\n"
596
    "      Translucent\n"
597
    "      Opaque\n"
598
    "      Open...\n"
599
    "    Width\n"
600
    "      1\n"
601
    "      2\n"
602
    "      4\n"
603
    "      8\n"
604
    "      16\n"
605
    "      Dialog...\n"
606
    "    Undo\n"
607
    "    Help\n"
608
    "    Dismiss\n"
609
    "\n"
610
    "Choose a drawing primitive from the Element sub-menu.\n"
611
    "\n"
612
    "Choose a color from the Color sub-menu.  Additional\n"
613
    "colors can be specified with the color browser.\n"
614
    "\n"
615
    "If you choose the color browser and press Grab, you can\n"
616
    "select the color by moving the pointer to the desired\n"
617
    "color on the screen and press any button.  The transparent\n"
618
    "color updates the image matte channel and is useful for\n"
619
    "image compositing.\n"
620
    "\n"
621
    "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
622
    "Additional stipples can be specified with the file browser.\n"
623
    "Stipples obtained from the file browser must be on disk in the\n"
624
    "X11 bitmap format.\n"
625
    "\n"
626
    "Choose a width, if appropriate, from the Width sub-menu.  To\n"
627
    "choose a specific width select the Dialog widget.\n"
628
    "\n"
629
    "Choose a point in the Image window and press button 1 and\n"
630
    "hold.  Next, move the pointer to another location in the\n"
631
    "image.  As you move, a line connects the initial location and\n"
632
    "the pointer.  When you release the button, the image is\n"
633
    "updated with the primitive you just drew.  For polygons, the\n"
634
    "image is updated when you press and release the button without\n"
635
    "moving the pointer.\n"
636
    "\n"
637
    "To cancel image drawing, move the pointer back to the\n"
638
    "starting point of the line and release the button.\n"
639
  },
640
  DisplayHelp[] =
641
  {
642
    "BUTTONS\n"
643
    "  The effects of each button press is described below.  Three\n"
644
    "  buttons are required.  If you have a two button mouse,\n"
645
    "  button 1 and 3 are returned.  Press ALT and button 3 to\n"
646
    "  simulate button 2.\n"
647
    "\n"
648
    "  1    Press this button to map or unmap the Command widget.\n"
649
    "\n"
650
    "  2    Press and drag to define a region of the image to\n"
651
    "       magnify.\n"
652
    "\n"
653
    "  3    Press and drag to choose from a select set of commands.\n"
654
    "       This button behaves differently if the image being\n"
655
    "       displayed is a visual image directory.  Here, choose a\n"
656
    "       particular tile of the directory and press this button and\n"
657
    "       drag to select a command from a pop-up menu.  Choose from\n"
658
    "       these menu items:\n"
659
    "\n"
660
    "           Open\n"
661
    "           Next\n"
662
    "           Former\n"
663
    "           Delete\n"
664
    "           Update\n"
665
    "\n"
666
    "       If you choose Open, the image represented by the tile is\n"
667
    "       displayed.  To return to the visual image directory, choose\n"
668
    "       Next from the Command widget.  Next and Former moves to the\n"
669
    "       next or former image respectively.  Choose Delete to delete\n"
670
    "       a particular image tile.  Finally, choose Update to\n"
671
    "       synchronize all the image tiles with their respective\n"
672
    "       images.\n"
673
    "\n"
674
    "COMMAND WIDGET\n"
675
    "  The Command widget lists a number of sub-menus and commands.\n"
676
    "  They are\n"
677
    "\n"
678
    "      File\n"
679
    "        Open...\n"
680
    "        Next\n"
681
    "        Former\n"
682
    "        Select...\n"
683
    "        Save...\n"
684
    "        Print...\n"
685
    "        Delete...\n"
686
    "        New...\n"
687
    "        Visual Directory...\n"
688
    "        Quit\n"
689
    "      Edit\n"
690
    "        Undo\n"
691
    "        Redo\n"
692
    "        Cut\n"
693
    "        Copy\n"
694
    "        Paste\n"
695
    "      View\n"
696
    "        Half Size\n"
697
    "        Original Size\n"
698
    "        Double Size\n"
699
    "        Resize...\n"
700
    "        Apply\n"
701
    "        Refresh\n"
702
    "        Restore\n"
703
    "      Transform\n"
704
    "        Crop\n"
705
    "        Chop\n"
706
    "        Flop\n"
707
    "        Flip\n"
708
    "        Rotate Right\n"
709
    "        Rotate Left\n"
710
    "        Rotate...\n"
711
    "        Shear...\n"
712
    "        Roll...\n"
713
    "        Trim Edges\n"
714
    "      Enhance\n"
715
    "        Brightness...\n"
716
    "        Saturation...\n"
717
    "        Hue...\n"
718
    "        Gamma...\n"
719
    "        Sharpen...\n"
720
    "        Dull\n"
721
    "        Contrast Stretch...\n"
722
    "        Sigmoidal Contrast...\n"
723
    "        Normalize\n"
724
    "        Equalize\n"
725
    "        Negate\n"
726
    "        Grayscale\n"
727
    "        Map...\n"
728
    "        Quantize...\n"
729
    "      Effects\n"
730
    "        Despeckle\n"
731
    "        Emboss\n"
732
    "        Reduce Noise\n"
733
    "        Add Noise\n"
734
    "        Sharpen...\n"
735
    "        Blur...\n"
736
    "        Threshold...\n"
737
    "        Edge Detect...\n"
738
    "        Spread...\n"
739
    "        Shade...\n"
740
    "        Painting...\n"
741
    "        Segment...\n"
742
    "      F/X\n"
743
    "        Solarize...\n"
744
    "        Sepia Tone...\n"
745
    "        Swirl...\n"
746
    "        Implode...\n"
747
    "        Vignette...\n"
748
    "        Wave...\n"
749
    "        Oil Painting...\n"
750
    "        Charcoal Drawing...\n"
751
    "      Image Edit\n"
752
    "        Annotate...\n"
753
    "        Draw...\n"
754
    "        Color...\n"
755
    "        Matte...\n"
756
    "        Composite...\n"
757
    "        Add Border...\n"
758
    "        Add Frame...\n"
759
    "        Comment...\n"
760
    "        Launch...\n"
761
    "        Region of Interest...\n"
762
    "      Miscellany\n"
763
    "        Image Info\n"
764
    "        Zoom Image\n"
765
    "        Show Preview...\n"
766
    "        Show Histogram\n"
767
    "        Show Matte\n"
768
    "        Background...\n"
769
    "        Slide Show\n"
770
    "        Preferences...\n"
771
    "      Help\n"
772
    "        Overview\n"
773
    "        Browse Documentation\n"
774
    "        About Display\n"
775
    "\n"
776
    "  Menu items with a indented triangle have a sub-menu.  They\n"
777
    "  are represented above as the indented items.  To access a\n"
778
    "  sub-menu item, move the pointer to the appropriate menu and\n"
779
    "  press a button and drag.  When you find the desired sub-menu\n"
780
    "  item, release the button and the command is executed.  Move\n"
781
    "  the pointer away from the sub-menu if you decide not to\n"
782
    "  execute a particular command.\n"
783
    "\n"
784
    "KEYBOARD ACCELERATORS\n"
785
    "  Accelerators are one or two key presses that effect a\n"
786
    "  particular command.  The keyboard accelerators that\n"
787
    "  display(1) understands is:\n"
788
    "\n"
789
    "  Ctl+O     Press to open an image from a file.\n"
790
    "\n"
791
    "  space     Press to display the next image.\n"
792
    "\n"
793
    "            If the image is a multi-paged document such as a Postscript\n"
794
    "            document, you can skip ahead several pages by preceding\n"
795
    "            this command with a number.  For example to display the\n"
796
    "            third page beyond the current page, press 3<space>.\n"
797
    "\n"
798
    "  backspace Press to display the former image.\n"
799
    "\n"
800
    "            If the image is a multi-paged document such as a Postscript\n"
801
    "            document, you can skip behind several pages by preceding\n"
802
    "            this command with a number.  For example to display the\n"
803
    "            third page preceding the current page, press 3<backspace>.\n"
804
    "\n"
805
    "  Ctl+S     Press to write the image to a file.\n"
806
    "\n"
807
    "  Ctl+P     Press to print the image to a Postscript printer.\n"
808
    "\n"
809
    "  Ctl+D     Press to delete an image file.\n"
810
    "\n"
811
    "  Ctl+N     Press to create a blank canvas.\n"
812
    "\n"
813
    "  Ctl+Q     Press to discard all images and exit program.\n"
814
    "\n"
815
    "  Ctl+Z     Press to undo last image transformation.\n"
816
    "\n"
817
    "  Ctl+R     Press to redo last image transformation.\n"
818
    "\n"
819
    "  Ctl+X     Press to cut a region of the image.\n"
820
    "\n"
821
    "  Ctl+C     Press to copy a region of the image.\n"
822
    "\n"
823
    "  Ctl+V     Press to paste a region to the image.\n"
824
    "\n"
825
    "  <         Press to half the image size.\n"
826
    "\n"
827
    "  -         Press to return to the original image size.\n"
828
    "\n"
829
    "  >         Press to double the image size.\n"
830
    "\n"
831
    "  %         Press to resize the image to a width and height you\n"
832
    "            specify.\n"
833
    "\n"
834
    "Cmd-A       Press to make any image transformations permanent."
835
    "\n"
836
    "            By default, any image size transformations are applied\n"
837
    "            to the original image to create the image displayed on\n"
838
    "            the X server.  However, the transformations are not\n"
839
    "            permanent (i.e. the original image does not change\n"
840
    "            size only the X image does).  For example, if you\n"
841
    "            press > the X image will appear to double in size,\n"
842
    "            but the original image will in fact remain the same size.\n"
843
    "            To force the original image to double in size, press >\n"
844
    "            followed by Cmd-A.\n"
845
    "\n"
846
    "  @         Press to refresh the image window.\n"
847
    "\n"
848
    "  C         Press to cut out a rectangular region of the image.\n"
849
    "\n"
850
    "  [         Press to chop the image.\n"
851
    "\n"
852
    "  H         Press to flop image in the horizontal direction.\n"
853
    "\n"
854
    "  V         Press to flip image in the vertical direction.\n"
855
    "\n"
856
    "  /         Press to rotate the image 90 degrees clockwise.\n"
857
    "\n"
858
    " \\         Press to rotate the image 90 degrees counter-clockwise.\n"
859
    "\n"
860
    "  *         Press to rotate the image the number of degrees you\n"
861
    "            specify.\n"
862
    "\n"
863
    "  S         Press to shear the image the number of degrees you\n"
864
    "            specify.\n"
865
    "\n"
866
    "  R         Press to roll the image.\n"
867
    "\n"
868
    "  T         Press to trim the image edges.\n"
869
    "\n"
870
    "  Shft-H    Press to vary the image hue.\n"
871
    "\n"
872
    "  Shft-S    Press to vary the color saturation.\n"
873
    "\n"
874
    "  Shft-L    Press to vary the color brightness.\n"
875
    "\n"
876
    "  Shft-G    Press to gamma correct the image.\n"
877
    "\n"
878
    "  Shft-C    Press to sharpen the image contrast.\n"
879
    "\n"
880
    "  Shft-Z    Press to dull the image contrast.\n"
881
    "\n"
882
    "  =         Press to perform histogram equalization on the image.\n"
883
    "\n"
884
    "  Shft-N    Press to perform histogram normalization on the image.\n"
885
    "\n"
886
    "  Shft-~    Press to negate the colors of the image.\n"
887
    "\n"
888
    "  .         Press to convert the image colors to gray.\n"
889
    "\n"
890
    "  Shft-#    Press to set the maximum number of unique colors in the\n"
891
    "            image.\n"
892
    "\n"
893
    "  F2        Press to reduce the speckles in an image.\n"
894
    "\n"
895
    "  F3        Press to eliminate peak noise from an image.\n"
896
    "\n"
897
    "  F4        Press to add noise to an image.\n"
898
    "\n"
899
    "  F5        Press to sharpen an image.\n"
900
    "\n"
901
    "  F6        Press to delete an image file.\n"
902
    "\n"
903
    "  F7        Press to threshold the image.\n"
904
    "\n"
905
    "  F8        Press to detect edges within an image.\n"
906
    "\n"
907
    "  F9        Press to emboss an image.\n"
908
    "\n"
909
    "  F10       Press to displace pixels by a random amount.\n"
910
    "\n"
911
    "  F11       Press to negate all pixels above the threshold level.\n"
912
    "\n"
913
    "  F12       Press to shade the image using a distant light source.\n"
914
    "\n"
915
    "  F13       Press to lighten or darken image edges to create a 3-D effect.\n"
916
    "\n"
917
    "  F14       Press to segment the image by color.\n"
918
    "\n"
919
    "  Meta-S    Press to swirl image pixels about the center.\n"
920
    "\n"
921
    "  Meta-I    Press to implode image pixels about the center.\n"
922
    "\n"
923
    "  Meta-W    Press to alter an image along a sine wave.\n"
924
    "\n"
925
    "  Meta-P    Press to simulate an oil painting.\n"
926
    "\n"
927
    "  Meta-C    Press to simulate a charcoal drawing.\n"
928
    "\n"
929
    "  Alt-A     Press to annotate the image with text.\n"
930
    "\n"
931
    "  Alt-D     Press to draw on an image.\n"
932
    "\n"
933
    "  Alt-P     Press to edit an image pixel color.\n"
934
    "\n"
935
    "  Alt-M     Press to edit the image matte information.\n"
936
    "\n"
937
    "  Alt-V     Press to composite the image with another.\n"
938
    "\n"
939
    "  Alt-B     Press to add a border to the image.\n"
940
    "\n"
941
    "  Alt-F     Press to add an ornamental border to the image.\n"
942
    "\n"
943
    "  Alt-Shft-!\n"
944
    "            Press to add an image comment.\n"
945
    "\n"
946
    "  Ctl-A     Press to apply image processing techniques to a region\n"
947
    "            of interest.\n"
948
    "\n"
949
    "  Shft-?    Press to display information about the image.\n"
950
    "\n"
951
    "  Shft-+    Press to map the zoom image window.\n"
952
    "\n"
953
    "  Shft-P    Press to preview an image enhancement, effect, or f/x.\n"
954
    "\n"
955
    "  F1        Press to display helpful information about display(1).\n"
956
    "\n"
957
    "  Find      Press to browse documentation about ImageMagick.\n"
958
    "\n"
959
    "  1-9       Press to change the level of magnification.\n"
960
    "\n"
961
    "  Use the arrow keys to move the image one pixel up, down,\n"
962
    "  left, or right within the magnify window.  Be sure to first\n"
963
    "  map the magnify window by pressing button 2.\n"
964
    "\n"
965
    "  Press ALT and one of the arrow keys to trim off one pixel\n"
966
    "  from any side of the image.\n"
967
  },
968
  ImageMatteEditHelp[] =
969
  {
970
    "Matte information within an image is useful for some\n"
971
    "operations such as image compositing (See IMAGE\n"
972
    "COMPOSITING).  This extra channel usually defines a mask\n"
973
    "which represents a sort of a cookie-cutter for the image.\n"
974
    "This the case when matte is opaque (full coverage) for\n"
975
    "pixels inside the shape, zero outside, and between 0 and\n"
976
    "QuantumRange on the boundary.\n"
977
    "\n"
978
    "A small window appears showing the location of the cursor in\n"
979
    "the image window. You are now in matte edit mode.  To exit\n"
980
    "immediately, press Dismiss.  In matte edit mode, the Command\n"
981
    "widget has these options:\n"
982
    "\n"
983
    "    Method\n"
984
    "      point\n"
985
    "      replace\n"
986
    "      floodfill\n"
987
    "      filltoborder\n"
988
    "      reset\n"
989
    "    Border Color\n"
990
    "      black\n"
991
    "      blue\n"
992
    "      cyan\n"
993
    "      green\n"
994
    "      gray\n"
995
    "      red\n"
996
    "      magenta\n"
997
    "      yellow\n"
998
    "      white\n"
999
    "      Browser...\n"
1000
    "    Fuzz\n"
1001
    "      0%\n"
1002
    "      2%\n"
1003
    "      5%\n"
1004
    "      10%\n"
1005
    "      15%\n"
1006
    "      Dialog...\n"
1007
    "    Matte\n"
1008
    "      Opaque\n"
1009
    "      Transparent\n"
1010
    "      Dialog...\n"
1011
    "    Undo\n"
1012
    "    Help\n"
1013
    "    Dismiss\n"
1014
    "\n"
1015
    "Choose a matte editing method from the Method sub-menu of\n"
1016
    "the Command widget.  The point method changes the matte value\n"
1017
    "of any pixel selected with the pointer until the button is\n"
1018
    "is released.  The replace method changes the matte value of\n"
1019
    "any pixel that matches the color of the pixel you select with\n"
1020
    "a button press.  Floodfill changes the matte value of any pixel\n"
1021
    "that matches the color of the pixel you select with a button\n"
1022
    "press and is a neighbor.  Whereas filltoborder changes the matte\n"
1023
    "value any neighbor pixel that is not the border color.  Finally\n"
1024
    "reset changes the entire image to the designated matte value.\n"
1025
    "\n"
1026
    "Choose Matte Value and pick Opaque or Transparent.  For other values\n"
1027
    "select the Dialog entry.  Here a dialog appears requesting a matte\n"
1028
    "value.  The value you select is assigned as the opacity value of the\n"
1029
    "selected pixel or pixels.\n"
1030
    "\n"
1031
    "Now, press any button to select a pixel within the image\n"
1032
    "window to change its matte value.\n"
1033
    "\n"
1034
    "If the Magnify widget is mapped, it can be helpful in positioning\n"
1035
    "your pointer within the image (refer to button 2).\n"
1036
    "\n"
1037
    "Matte information is only valid in a DirectClass image.\n"
1038
    "Therefore, any PseudoClass image is promoted to DirectClass\n"
1039
    "(see miff(5)).  Note that matte information for PseudoClass\n"
1040
    "is not retained for colormapped X server visuals (e.g.\n"
1041
    "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1042
    "immediately save your image to a file (refer to Write).\n"
1043
    "Correct matte editing behavior may require a TrueColor or\n"
1044
    "DirectColor visual or a Standard Colormap.\n"
1045
  },
1046
  ImagePanHelp[] =
1047
  {
1048
    "When an image exceeds the width or height of the X server\n"
1049
    "screen, display maps a small panning icon.  The rectangle\n"
1050
    "within the panning icon shows the area that is currently\n"
1051
    "displayed in the image window.  To pan about the image,\n"
1052
    "press any button and drag the pointer within the panning\n"
1053
    "icon.  The pan rectangle moves with the pointer and the\n"
1054
    "image window is updated to reflect the location of the\n"
1055
    "rectangle within the panning icon.  When you have selected\n"
1056
    "the area of the image you wish to view, release the button.\n"
1057
    "\n"
1058
    "Use the arrow keys to pan the image one pixel up, down,\n"
1059
    "left, or right within the image window.\n"
1060
    "\n"
1061
    "The panning icon is withdrawn if the image becomes smaller\n"
1062
    "than the dimensions of the X server screen.\n"
1063
  },
1064
  ImagePasteHelp[] =
1065
  {
1066
    "A small window appears showing the location of the cursor in\n"
1067
    "the image window. You are now in paste mode.  To exit\n"
1068
    "immediately, press Dismiss.  In paste mode, the Command\n"
1069
    "widget has these options:\n"
1070
    "\n"
1071
    "    Operators\n"
1072
    "      over\n"
1073
    "      in\n"
1074
    "      out\n"
1075
    "      atop\n"
1076
    "      xor\n"
1077
    "      plus\n"
1078
    "      minus\n"
1079
    "      add\n"
1080
    "      subtract\n"
1081
    "      difference\n"
1082
    "      replace\n"
1083
    "    Help\n"
1084
    "    Dismiss\n"
1085
    "\n"
1086
    "Choose a composite operation from the Operators sub-menu of\n"
1087
    "the Command widget.  How each operator behaves is described\n"
1088
    "below.  Image window is the image currently displayed on\n"
1089
    "your X server and image is the image obtained with the File\n"
1090
    "Browser widget.\n"
1091
    "\n"
1092
    "Over     The result is the union of the two image shapes,\n"
1093
    "         with image obscuring image window in the region of\n"
1094
    "         overlap.\n"
1095
    "\n"
1096
    "In       The result is simply image cut by the shape of\n"
1097
    "         image window.  None of the image data of image\n"
1098
    "         window is in the result.\n"
1099
    "\n"
1100
    "Out      The resulting image is image with the shape of\n"
1101
    "         image window cut out.\n"
1102
    "\n"
1103
    "Atop     The result is the same shape as the image window,\n"
1104
    "         with image obscuring image window where the image\n"
1105
    "         shapes overlap.  Note this differs from over\n"
1106
    "         because the portion of image outside image window's\n"
1107
    "         shape does not appear in the result.\n"
1108
    "\n"
1109
    "Xor      The result is the image data from both image and\n"
1110
    "         image window that is outside the overlap region.\n"
1111
    "         The overlap region is blank.\n"
1112
    "\n"
1113
    "Plus     The result is just the sum of the image data.\n"
1114
    "         Output values are cropped to QuantumRange (no overflow).\n"
1115
    "         This operation is independent of the matte\n"
1116
    "         channels.\n"
1117
    "\n"
1118
    "Minus    The result of image - image window, with underflow\n"
1119
    "         cropped to zero.\n"
1120
    "\n"
1121
    "Add      The result of image + image window, with overflow\n"
1122
    "         wrapping around (mod 256).\n"
1123
    "\n"
1124
    "Subtract The result of image - image window, with underflow\n"
1125
    "         wrapping around (mod 256).  The add and subtract\n"
1126
    "         operators can be used to perform reversible\n"
1127
    "         transformations.\n"
1128
    "\n"
1129
    "Difference\n"
1130
    "         The result of abs(image - image window).  This\n"
1131
    "         useful for comparing two very similar images.\n"
1132
    "\n"
1133
    "Copy     The resulting image is image window replaced with\n"
1134
    "         image.  Here the matte information is ignored.\n"
1135
    "\n"
1136
    "CopyRed  The red layer of the image window is replace with\n"
1137
    "         the red layer of the image.  The other layers are\n"
1138
    "         untouched.\n"
1139
    "\n"
1140
    "CopyGreen\n"
1141
    "         The green layer of the image window is replace with\n"
1142
    "         the green layer of the image.  The other layers are\n"
1143
    "         untouched.\n"
1144
    "\n"
1145
    "CopyBlue The blue layer of the image window is replace with\n"
1146
    "         the blue layer of the image.  The other layers are\n"
1147
    "         untouched.\n"
1148
    "\n"
1149
    "CopyOpacity\n"
1150
    "         The matte layer of the image window is replace with\n"
1151
    "         the matte layer of the image.  The other layers are\n"
1152
    "         untouched.\n"
1153
    "\n"
1154
    "The image compositor requires a matte, or alpha channel in\n"
1155
    "the image for some operations.  This extra channel usually\n"
1156
    "defines a mask which represents a sort of a cookie-cutter\n"
1157
    "for the image.  This the case when matte is opaque (full\n"
1158
    "coverage) for pixels inside the shape, zero outside, and\n"
1159
    "between 0 and QuantumRange on the boundary.  If image does not\n"
1160
    "have a matte channel, it is initialized with 0 for any pixel\n"
1161
    "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1162
    "\n"
1163
    "Note that matte information for image window is not retained\n"
1164
    "for colormapped X server visuals (e.g. StaticColor,\n"
1165
    "StaticColor, GrayScale, PseudoColor).  Correct compositing\n"
1166
    "behavior may require a TrueColor or DirectColor visual or a\n"
1167
    "Standard Colormap.\n"
1168
    "\n"
1169
    "Choosing a composite operator is optional.  The default\n"
1170
    "operator is replace.  However, you must choose a location to\n"
1171
    "paste your image and press button 1.  Press and hold the\n"
1172
    "button before releasing and an outline of the image will\n"
1173
    "appear to help you identify your location.\n"
1174
    "\n"
1175
    "The actual colors of the pasted image is saved.  However,\n"
1176
    "the color that appears in image window may be different.\n"
1177
    "For example, on a monochrome screen image window will appear\n"
1178
    "black or white even though your pasted image may have\n"
1179
    "many colors.  If the image is saved to a file it is written\n"
1180
    "with the correct colors.  To assure the correct colors are\n"
1181
    "saved in the final image, any PseudoClass image is promoted\n"
1182
    "to DirectClass (see miff(5)).  To force a PseudoClass image\n"
1183
    "to remain PseudoClass, use -colors.\n"
1184
  },
1185
  ImageROIHelp[] =
1186
  {
1187
    "In region of interest mode, the Command widget has these\n"
1188
    "options:\n"
1189
    "\n"
1190
    "    Help\n"
1191
    "    Dismiss\n"
1192
    "\n"
1193
    "To define a region of interest, press button 1 and drag.\n"
1194
    "The region of interest is defined by a highlighted rectangle\n"
1195
    "that expands or contracts as it follows the pointer.  Once\n"
1196
    "you are satisfied with the region of interest, release the\n"
1197
    "button.  You are now in apply mode.  In apply mode the\n"
1198
    "Command widget has these options:\n"
1199
    "\n"
1200
    "      File\n"
1201
    "        Save...\n"
1202
    "        Print...\n"
1203
    "      Edit\n"
1204
    "        Undo\n"
1205
    "        Redo\n"
1206
    "      Transform\n"
1207
    "        Flop\n"
1208
    "        Flip\n"
1209
    "        Rotate Right\n"
1210
    "        Rotate Left\n"
1211
    "      Enhance\n"
1212
    "        Hue...\n"
1213
    "        Saturation...\n"
1214
    "        Brightness...\n"
1215
    "        Gamma...\n"
1216
    "        Spiff\n"
1217
    "        Dull\n"
1218
    "        Contrast Stretch\n"
1219
    "        Sigmoidal Contrast...\n"
1220
    "        Normalize\n"
1221
    "        Equalize\n"
1222
    "        Negate\n"
1223
    "        Grayscale\n"
1224
    "        Map...\n"
1225
    "        Quantize...\n"
1226
    "      Effects\n"
1227
    "        Despeckle\n"
1228
    "        Emboss\n"
1229
    "        Reduce Noise\n"
1230
    "        Sharpen...\n"
1231
    "        Blur...\n"
1232
    "        Threshold...\n"
1233
    "        Edge Detect...\n"
1234
    "        Spread...\n"
1235
    "        Shade...\n"
1236
    "        Raise...\n"
1237
    "        Segment...\n"
1238
    "      F/X\n"
1239
    "        Solarize...\n"
1240
    "        Sepia Tone...\n"
1241
    "        Swirl...\n"
1242
    "        Implode...\n"
1243
    "        Vignette...\n"
1244
    "        Wave...\n"
1245
    "        Oil Painting...\n"
1246
    "        Charcoal Drawing...\n"
1247
    "      Miscellany\n"
1248
    "        Image Info\n"
1249
    "        Zoom Image\n"
1250
    "        Show Preview...\n"
1251
    "        Show Histogram\n"
1252
    "        Show Matte\n"
1253
    "      Help\n"
1254
    "      Dismiss\n"
1255
    "\n"
1256
    "You can make adjustments to the region of interest by moving\n"
1257
    "the pointer to one of the rectangle corners, pressing a\n"
1258
    "button, and dragging.  Finally, choose an image processing\n"
1259
    "technique from the Command widget.  You can choose more than\n"
1260
    "one image processing technique to apply to an area.\n"
1261
    "Alternatively, you can move the region of interest before\n"
1262
    "applying another image processing technique.  To exit, press\n"
1263
    "Dismiss.\n"
1264
  },
1265
  ImageRotateHelp[] =
1266
  {
1267
    "In rotate mode, the Command widget has these options:\n"
1268
    "\n"
1269
    "    Pixel Color\n"
1270
    "      black\n"
1271
    "      blue\n"
1272
    "      cyan\n"
1273
    "      green\n"
1274
    "      gray\n"
1275
    "      red\n"
1276
    "      magenta\n"
1277
    "      yellow\n"
1278
    "      white\n"
1279
    "      Browser...\n"
1280
    "    Direction\n"
1281
    "      horizontal\n"
1282
    "      vertical\n"
1283
    "    Help\n"
1284
    "    Dismiss\n"
1285
    "\n"
1286
    "Choose a background color from the Pixel Color sub-menu.\n"
1287
    "Additional background colors can be specified with the color\n"
1288
    "browser.  You can change the menu colors by setting the X\n"
1289
    "resources pen1 through pen9.\n"
1290
    "\n"
1291
    "If you choose the color browser and press Grab, you can\n"
1292
    "select the background color by moving the pointer to the\n"
1293
    "desired color on the screen and press any button.\n"
1294
    "\n"
1295
    "Choose a point in the image window and press this button and\n"
1296
    "hold.  Next, move the pointer to another location in the\n"
1297
    "image.  As you move a line connects the initial location and\n"
1298
    "the pointer.  When you release the button, the degree of\n"
1299
    "image rotation is determined by the slope of the line you\n"
1300
    "just drew.  The slope is relative to the direction you\n"
1301
    "choose from the Direction sub-menu of the Command widget.\n"
1302
    "\n"
1303
    "To cancel the image rotation, move the pointer back to the\n"
1304
    "starting point of the line and release the button.\n"
1305
  };
1306

1307
/*
1308
  Enumeration declarations.
1309
*/
1310
typedef enum
1311
{
1312
  CopyMode,
1313
  CropMode,
1314
  CutMode
1315
} ClipboardMode;
1316
1317
typedef enum
1318
{
1319
  OpenCommand,
1320
  NextCommand,
1321
  FormerCommand,
1322
  SelectCommand,
1323
  SaveCommand,
1324
  PrintCommand,
1325
  DeleteCommand,
1326
  NewCommand,
1327
  VisualDirectoryCommand,
1328
  QuitCommand,
1329
  UndoCommand,
1330
  RedoCommand,
1331
  CutCommand,
1332
  CopyCommand,
1333
  PasteCommand,
1334
  HalfSizeCommand,
1335
  OriginalSizeCommand,
1336
  DoubleSizeCommand,
1337
  ResizeCommand,
1338
  ApplyCommand,
1339
  RefreshCommand,
1340
  RestoreCommand,
1341
  CropCommand,
1342
  ChopCommand,
1343
  FlopCommand,
1344
  FlipCommand,
1345
  RotateRightCommand,
1346
  RotateLeftCommand,
1347
  RotateCommand,
1348
  ShearCommand,
1349
  RollCommand,
1350
  TrimCommand,
1351
  HueCommand,
1352
  SaturationCommand,
1353
  BrightnessCommand,
1354
  GammaCommand,
1355
  SpiffCommand,
1356
  DullCommand,
1357
  ContrastStretchCommand,
1358
  SigmoidalContrastCommand,
1359
  NormalizeCommand,
1360
  EqualizeCommand,
1361
  NegateCommand,
1362
  GrayscaleCommand,
1363
  MapCommand,
1364
  QuantizeCommand,
1365
  DespeckleCommand,
1366
  EmbossCommand,
1367
  ReduceNoiseCommand,
1368
  AddNoiseCommand,
1369
  SharpenCommand,
1370
  BlurCommand,
1371
  ThresholdCommand,
1372
  EdgeDetectCommand,
1373
  SpreadCommand,
1374
  ShadeCommand,
1375
  RaiseCommand,
1376
  SegmentCommand,
1377
  SolarizeCommand,
1378
  SepiaToneCommand,
1379
  SwirlCommand,
1380
  ImplodeCommand,
1381
  VignetteCommand,
1382
  WaveCommand,
1383
  OilPaintCommand,
1384
  CharcoalDrawCommand,
1385
  AnnotateCommand,
1386
  DrawCommand,
1387
  ColorCommand,
1388
  MatteCommand,
1389
  CompositeCommand,
1390
  AddBorderCommand,
1391
  AddFrameCommand,
1392
  CommentCommand,
1393
  LaunchCommand,
1394
  RegionOfInterestCommand,
1395
  ROIHelpCommand,
1396
  ROIDismissCommand,
1397
  InfoCommand,
1398
  ZoomCommand,
1399
  ShowPreviewCommand,
1400
  ShowHistogramCommand,
1401
  ShowMatteCommand,
1402
  BackgroundCommand,
1403
  SlideShowCommand,
1404
  PreferencesCommand,
1405
  HelpCommand,
1406
  BrowseDocumentationCommand,
1407
  VersionCommand,
1408
  SaveToUndoBufferCommand,
1409
  FreeBuffersCommand,
1410
  NullCommand
1411
} DisplayCommand;
1412
1413
typedef enum
1414
{
1415
  AnnotateNameCommand,
1416
  AnnotateFontColorCommand,
1417
  AnnotateBackgroundColorCommand,
1418
  AnnotateRotateCommand,
1419
  AnnotateHelpCommand,
1420
  AnnotateDismissCommand,
1421
  TextHelpCommand,
1422
  TextApplyCommand,
1423
  ChopDirectionCommand,
1424
  ChopHelpCommand,
1425
  ChopDismissCommand,
1426
  HorizontalChopCommand,
1427
  VerticalChopCommand,
1428
  ColorEditMethodCommand,
1429
  ColorEditColorCommand,
1430
  ColorEditBorderCommand,
1431
  ColorEditFuzzCommand,
1432
  ColorEditUndoCommand,
1433
  ColorEditHelpCommand,
1434
  ColorEditDismissCommand,
1435
  CompositeOperatorsCommand,
1436
  CompositeDissolveCommand,
1437
  CompositeDisplaceCommand,
1438
  CompositeHelpCommand,
1439
  CompositeDismissCommand,
1440
  CropHelpCommand,
1441
  CropDismissCommand,
1442
  RectifyCopyCommand,
1443
  RectifyHelpCommand,
1444
  RectifyDismissCommand,
1445
  DrawElementCommand,
1446
  DrawColorCommand,
1447
  DrawStippleCommand,
1448
  DrawWidthCommand,
1449
  DrawUndoCommand,
1450
  DrawHelpCommand,
1451
  DrawDismissCommand,
1452
  MatteEditMethod,
1453
  MatteEditBorderCommand,
1454
  MatteEditFuzzCommand,
1455
  MatteEditValueCommand,
1456
  MatteEditUndoCommand,
1457
  MatteEditHelpCommand,
1458
  MatteEditDismissCommand,
1459
  PasteOperatorsCommand,
1460
  PasteHelpCommand,
1461
  PasteDismissCommand,
1462
  RotateColorCommand,
1463
  RotateDirectionCommand,
1464
  RotateCropCommand,
1465
  RotateSharpenCommand,
1466
  RotateHelpCommand,
1467
  RotateDismissCommand,
1468
  HorizontalRotateCommand,
1469
  VerticalRotateCommand,
1470
  TileLoadCommand,
1471
  TileNextCommand,
1472
  TileFormerCommand,
1473
  TileDeleteCommand,
1474
  TileUpdateCommand
1475
} ModeType;
1476

1477
/*
1478
  Stipples.
1479
*/
1480
#define BricksWidth  20
1481
#define BricksHeight  20
1482
#define DiagonalWidth  16
1483
#define DiagonalHeight  16
1484
#define HighlightWidth  8
1485
#define HighlightHeight  8
1486
#define OpaqueWidth  8
1487
#define OpaqueHeight  8
1488
#define ScalesWidth  16
1489
#define ScalesHeight  16
1490
#define ShadowWidth  8
1491
#define ShadowHeight  8
1492
#define VerticalWidth  16
1493
#define VerticalHeight  16
1494
#define WavyWidth  16
1495
#define WavyHeight  16
1496

1497
/*
1498
  Constant declaration.
1499
*/
1500
static const int
1501
  RoiDelta = 8;
1502
1503
static const unsigned char
1504
  BricksBitmap[] =
1505
  {
1506
    0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1507
    0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1508
    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1509
    0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1510
    0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1511
  },
1512
  DiagonalBitmap[] =
1513
  {
1514
    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1515
    0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1516
    0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1517
  },
1518
  ScalesBitmap[] =
1519
  {
1520
    0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1521
    0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1522
    0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1523
  },
1524
  VerticalBitmap[] =
1525
  {
1526
    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1527
    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1528
    0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1529
  },
1530
  WavyBitmap[] =
1531
  {
1532
    0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1533
    0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1534
    0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1535
  };
1536

1537
/*
1538
  Function prototypes.
1539
*/
1540
static DisplayCommand
1541
  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1542
    const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1543
1544
static Image
1545
  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const DisplayCommand,
1546
    Image **,ExceptionInfo *),
1547
  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1548
  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1549
    ExceptionInfo *),
1550
  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1551
    ExceptionInfo *);
1552
1553
static MagickBooleanType
1554
  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1555
    ExceptionInfo *),
1556
  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1557
    ExceptionInfo *),
1558
  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1559
    ExceptionInfo *),
1560
  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1561
    ExceptionInfo *),
1562
  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1563
    ExceptionInfo *),
1564
  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1565
    ExceptionInfo *),
1566
  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1567
  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1568
    ExceptionInfo *),
1569
  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1570
    ExceptionInfo *),
1571
  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1572
  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1573
  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1574
    ExceptionInfo *),
1575
  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1576
  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1577
  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1578
1579
static void
1580
  XDrawPanRectangle(Display *,XWindows *),
1581
  XImageCache(Display *,XResourceInfo *,XWindows *,const DisplayCommand,Image **,
1582
    ExceptionInfo *),
1583
  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1584
  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1585
  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1586
  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1587
    const KeySym,ExceptionInfo *),
1588
  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1589
  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1590
  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1591

1592
/*
1593
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1594
%                                                                             %
1595
%                                                                             %
1596
%                                                                             %
1597
%   D i s p l a y I m a g e s                                                 %
1598
%                                                                             %
1599
%                                                                             %
1600
%                                                                             %
1601
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602
%
1603
%  DisplayImages() displays an image sequence to any X window screen.  It
1604
%  returns a value other than 0 if successful.  Check the exception member
1605
%  of image to determine the reason for any failure.
1606
%
1607
%  The format of the DisplayImages method is:
1608
%
1609
%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1610
%        Image *images,ExceptionInfo *exception)
1611
%
1612
%  A description of each parameter follows:
1613
%
1614
%    o image_info: the image info.
1615
%
1616
%    o image: the image.
1617
%
1618
%    o exception: return any errors or warnings in this structure.
1619
%
1620
*/
1621
MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1622
  Image *images,ExceptionInfo *exception)
1623
{
1624
  char
1625
    *argv[1];
1626
1627
  Display
1628
    *display;
1629
1630
  Image
1631
    *image;
1632
1633
  size_t
1634
    state;
1635
1636
  ssize_t
1637
    i;
1638
1639
  XrmDatabase
1640
    resource_database;
1641
1642
  XResourceInfo
1643
    resource_info;
1644
1645
  assert(image_info != (const ImageInfo *) NULL);
1646
  assert(image_info->signature == MagickCoreSignature);
1647
  assert(images != (Image *) NULL);
1648
  assert(images->signature == MagickCoreSignature);
1649
  if (IsEventLogging() != MagickFalse)
1650
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1651
  display=XOpenDisplay(image_info->server_name);
1652
  if (display == (Display *) NULL)
1653
    {
1654
      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1655
        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1656
      return(MagickFalse);
1657
    }
1658
  if (exception->severity != UndefinedException)
1659
    CatchException(exception);
1660
  (void) XSetErrorHandler(XError);
1661
  resource_database=XGetResourceDatabase(display,GetClientName());
1662
  (void) memset(&resource_info,0,sizeof(resource_info));
1663
  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1664
  if (image_info->page != (char *) NULL)
1665
    resource_info.image_geometry=AcquireString(image_info->page);
1666
  resource_info.immutable=MagickTrue;
1667
  argv[0]=AcquireString(GetClientName());
1668
  state=DefaultState;
1669
  for (i=0; (state & ExitState) == 0; i++)
1670
  {
1671
    if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1672
      break;
1673
    image=GetImageFromList(images,i % (ssize_t) GetImageListLength(images));
1674
    (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1675
  }
1676
  (void) SetErrorHandler((ErrorHandler) NULL);
1677
  (void) SetWarningHandler((WarningHandler) NULL);
1678
  argv[0]=DestroyString(argv[0]);
1679
  (void) XCloseDisplay(display);
1680
  XDestroyResourceInfo(&resource_info);
1681
  if (exception->severity != UndefinedException)
1682
    return(MagickFalse);
1683
  return(MagickTrue);
1684
}
1685

1686
/*
1687
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1688
%                                                                             %
1689
%                                                                             %
1690
%                                                                             %
1691
%   R e m o t e D i s p l a y C o m m a n d                                   %
1692
%                                                                             %
1693
%                                                                             %
1694
%                                                                             %
1695
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1696
%
1697
%  RemoteDisplayCommand() encourages a remote display program to display the
1698
%  specified image filename.
1699
%
1700
%  The format of the RemoteDisplayCommand method is:
1701
%
1702
%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1703
%        const char *window,const char *filename,ExceptionInfo *exception)
1704
%
1705
%  A description of each parameter follows:
1706
%
1707
%    o image_info: the image info.
1708
%
1709
%    o window: Specifies the name or id of an X window.
1710
%
1711
%    o filename: the name of the image filename to display.
1712
%
1713
%    o exception: return any errors or warnings in this structure.
1714
%
1715
*/
1716
MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1717
  const char *window,const char *filename,ExceptionInfo *exception)
1718
{
1719
  Display
1720
    *display;
1721
1722
  MagickStatusType
1723
    status;
1724
1725
  assert(image_info != (const ImageInfo *) NULL);
1726
  assert(image_info->signature == MagickCoreSignature);
1727
  assert(filename != (char *) NULL);
1728
  if (IsEventLogging() != MagickFalse)
1729
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1730
  display=XOpenDisplay(image_info->server_name);
1731
  if (display == (Display *) NULL)
1732
    {
1733
      (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1734
        "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1735
      return(MagickFalse);
1736
    }
1737
  (void) XSetErrorHandler(XError);
1738
  status=XRemoteCommand(display,window,filename);
1739
  (void) XCloseDisplay(display);
1740
  return(status != 0 ? MagickTrue : MagickFalse);
1741
}
1742

1743
/*
1744
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1745
%                                                                             %
1746
%                                                                             %
1747
%                                                                             %
1748
+   X A n n o t a t e E d i t I m a g e                                       %
1749
%                                                                             %
1750
%                                                                             %
1751
%                                                                             %
1752
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1753
%
1754
%  XAnnotateEditImage() annotates the image with text.
1755
%
1756
%  The format of the XAnnotateEditImage method is:
1757
%
1758
%      MagickBooleanType XAnnotateEditImage(Display *display,
1759
%        XResourceInfo *resource_info,XWindows *windows,Image *image,
1760
%        ExceptionInfo *exception)
1761
%
1762
%  A description of each parameter follows:
1763
%
1764
%    o display: Specifies a connection to an X server;  returned from
1765
%      XOpenDisplay.
1766
%
1767
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1768
%
1769
%    o windows: Specifies a pointer to a XWindows structure.
1770
%
1771
%    o image: the image; returned from ReadImage.
1772
%
1773
*/
1774
1775
static MagickBooleanType XAnnotateEditImage(Display *display,
1776
  XResourceInfo *resource_info,XWindows *windows,Image *image,
1777
  ExceptionInfo *exception)
1778
{
1779
  const char
1780
    *const AnnotateMenu[] =
1781
    {
1782
      "Font Name",
1783
      "Font Color",
1784
      "Box Color",
1785
      "Rotate Text",
1786
      "Help",
1787
      "Dismiss",
1788
      (char *) NULL
1789
    },
1790
    *const TextMenu[] =
1791
    {
1792
      "Help",
1793
      "Apply",
1794
      (char *) NULL
1795
    };
1796
1797
  static const ModeType
1798
    AnnotateCommands[] =
1799
    {
1800
      AnnotateNameCommand,
1801
      AnnotateFontColorCommand,
1802
      AnnotateBackgroundColorCommand,
1803
      AnnotateRotateCommand,
1804
      AnnotateHelpCommand,
1805
      AnnotateDismissCommand
1806
    },
1807
    TextCommands[] =
1808
    {
1809
      TextHelpCommand,
1810
      TextApplyCommand
1811
    };
1812
1813
  static MagickBooleanType
1814
    transparent_box = MagickTrue,
1815
    transparent_pen = MagickFalse;
1816
1817
  static double
1818
    degrees = 0.0;
1819
1820
  static unsigned int
1821
    box_id = MaxNumberPens-2,
1822
    font_id = 0,
1823
    pen_id = 0;
1824
1825
  char
1826
    command[MagickPathExtent],
1827
    *p,
1828
    text[MagickPathExtent];
1829
1830
  const char
1831
    *ColorMenu[MaxNumberPens+1];
1832
1833
  Cursor
1834
    cursor;
1835
1836
  GC
1837
    annotate_context;
1838
1839
  int
1840
    id,
1841
    pen_number,
1842
    status,
1843
    x,
1844
    y;
1845
1846
  KeySym
1847
    key_symbol;
1848
1849
  size_t
1850
    state;
1851
1852
  ssize_t
1853
    i;
1854
1855
  unsigned int
1856
    height,
1857
    width;
1858
1859
  XAnnotateInfo
1860
    *annotate_info,
1861
    *previous_info;
1862
1863
  XColor
1864
    color;
1865
1866
  XFontStruct
1867
    *font_info;
1868
1869
  XEvent
1870
    event,
1871
    text_event;
1872
1873
  /*
1874
    Map Command widget.
1875
  */
1876
  (void) CloneString(&windows->command.name,"Annotate");
1877
  windows->command.data=4;
1878
  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1879
  (void) XMapRaised(display,windows->command.id);
1880
  XClientMessage(display,windows->image.id,windows->im_protocols,
1881
    windows->im_update_widget,CurrentTime);
1882
  /*
1883
    Track pointer until button 1 is pressed.
1884
  */
1885
  XQueryPosition(display,windows->image.id,&x,&y);
1886
  (void) XSelectInput(display,windows->image.id,
1887
    windows->image.attributes.event_mask | PointerMotionMask);
1888
  cursor=XCreateFontCursor(display,XC_left_side);
1889
  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1890
  state=DefaultState;
1891
  do
1892
  {
1893
    if (windows->info.mapped != MagickFalse)
1894
      {
1895
        /*
1896
          Display pointer position.
1897
        */
1898
        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1899
          x+windows->image.x,y+windows->image.y);
1900
        XInfoWidget(display,windows,text);
1901
      }
1902
    /*
1903
      Wait for next event.
1904
    */
1905
    XScreenEvent(display,windows,&event,exception);
1906
    if (event.xany.window == windows->command.id)
1907
      {
1908
        /*
1909
          Select a command from the Command widget.
1910
        */
1911
        id=XCommandWidget(display,windows,AnnotateMenu,&event);
1912
        (void) XCheckDefineCursor(display,windows->image.id,cursor);
1913
        if (id < 0)
1914
          continue;
1915
        switch (AnnotateCommands[id])
1916
        {
1917
          case AnnotateNameCommand:
1918
          {
1919
            const char
1920
              *FontMenu[MaxNumberFonts];
1921
1922
            int
1923
              font_number;
1924
1925
            /*
1926
              Initialize menu selections.
1927
            */
1928
            for (i=0; i < MaxNumberFonts; i++)
1929
              FontMenu[i]=resource_info->font_name[i];
1930
            FontMenu[MaxNumberFonts-2]="Browser...";
1931
            FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1932
            /*
1933
              Select a font name from the pop-up menu.
1934
            */
1935
            font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1936
              (const char **) FontMenu,command);
1937
            if (font_number < 0)
1938
              break;
1939
            if (font_number == (MaxNumberFonts-2))
1940
              {
1941
                static char
1942
                  font_name[MagickPathExtent] = "fixed";
1943
1944
                /*
1945
                  Select a font name from a browser.
1946
                */
1947
                resource_info->font_name[font_number]=font_name;
1948
                XFontBrowserWidget(display,windows,"Select",font_name);
1949
                if (*font_name == '\0')
1950
                  break;
1951
              }
1952
            /*
1953
              Initialize font info.
1954
            */
1955
            font_info=XLoadQueryFont(display,resource_info->font_name[
1956
              font_number]);
1957
            if (font_info == (XFontStruct *) NULL)
1958
              {
1959
                XNoticeWidget(display,windows,"Unable to load font:",
1960
                  resource_info->font_name[font_number]);
1961
                break;
1962
              }
1963
            font_id=(unsigned int) font_number;
1964
            (void) XFreeFont(display,font_info);
1965
            break;
1966
          }
1967
          case AnnotateFontColorCommand:
1968
          {
1969
            /*
1970
              Initialize menu selections.
1971
            */
1972
            for (i=0; i < (int) (MaxNumberPens-2); i++)
1973
              ColorMenu[i]=resource_info->pen_colors[i];
1974
            ColorMenu[MaxNumberPens-2]="transparent";
1975
            ColorMenu[MaxNumberPens-1]="Browser...";
1976
            ColorMenu[MaxNumberPens]=(const char *) NULL;
1977
            /*
1978
              Select a pen color from the pop-up menu.
1979
            */
1980
            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1981
              (const char **) ColorMenu,command);
1982
            if (pen_number < 0)
1983
              break;
1984
            transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1985
              MagickFalse;
1986
            if (transparent_pen != MagickFalse)
1987
              break;
1988
            if (pen_number == (MaxNumberPens-1))
1989
              {
1990
                static char
1991
                  color_name[MagickPathExtent] = "gray";
1992
1993
                /*
1994
                  Select a pen color from a dialog.
1995
                */
1996
                resource_info->pen_colors[pen_number]=color_name;
1997
                XColorBrowserWidget(display,windows,"Select",color_name);
1998
                if (*color_name == '\0')
1999
                  break;
2000
              }
2001
            /*
2002
              Set pen color.
2003
            */
2004
            (void) XParseColor(display,windows->map_info->colormap,
2005
              resource_info->pen_colors[pen_number],&color);
2006
            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2007
              (unsigned int) MaxColors,&color);
2008
            windows->pixel_info->pen_colors[pen_number]=color;
2009
            pen_id=(unsigned int) pen_number;
2010
            break;
2011
          }
2012
          case AnnotateBackgroundColorCommand:
2013
          {
2014
            /*
2015
              Initialize menu selections.
2016
            */
2017
            for (i=0; i < (int) (MaxNumberPens-2); i++)
2018
              ColorMenu[i]=resource_info->pen_colors[i];
2019
            ColorMenu[MaxNumberPens-2]="transparent";
2020
            ColorMenu[MaxNumberPens-1]="Browser...";
2021
            ColorMenu[MaxNumberPens]=(const char *) NULL;
2022
            /*
2023
              Select a pen color from the pop-up menu.
2024
            */
2025
            pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2026
              (const char **) ColorMenu,command);
2027
            if (pen_number < 0)
2028
              break;
2029
            transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2030
              MagickFalse;
2031
            if (transparent_box != MagickFalse)
2032
              break;
2033
            if (pen_number == (MaxNumberPens-1))
2034
              {
2035
                static char
2036
                  color_name[MagickPathExtent] = "gray";
2037
2038
                /*
2039
                  Select a pen color from a dialog.
2040
                */
2041
                resource_info->pen_colors[pen_number]=color_name;
2042
                XColorBrowserWidget(display,windows,"Select",color_name);
2043
                if (*color_name == '\0')
2044
                  break;
2045
              }
2046
            /*
2047
              Set pen color.
2048
            */
2049
            (void) XParseColor(display,windows->map_info->colormap,
2050
              resource_info->pen_colors[pen_number],&color);
2051
            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2052
              (unsigned int) MaxColors,&color);
2053
            windows->pixel_info->pen_colors[pen_number]=color;
2054
            box_id=(unsigned int) pen_number;
2055
            break;
2056
          }
2057
          case AnnotateRotateCommand:
2058
          {
2059
            int
2060
              entry;
2061
2062
            const char
2063
              *const RotateMenu[] =
2064
              {
2065
                "-90",
2066
                "-45",
2067
                "-30",
2068
                "0",
2069
                "30",
2070
                "45",
2071
                "90",
2072
                "180",
2073
                "Dialog...",
2074
                (char *) NULL,
2075
              };
2076
2077
            static char
2078
              angle[MagickPathExtent] = "30.0";
2079
2080
            /*
2081
              Select a command from the pop-up menu.
2082
            */
2083
            entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2084
              command);
2085
            if (entry < 0)
2086
              break;
2087
            if (entry != 8)
2088
              {
2089
                degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2090
                break;
2091
              }
2092
            (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2093
              angle);
2094
            if (*angle == '\0')
2095
              break;
2096
            degrees=StringToDouble(angle,(char **) NULL);
2097
            break;
2098
          }
2099
          case AnnotateHelpCommand:
2100
          {
2101
            XTextViewHelp(display,resource_info,windows,MagickFalse,
2102
              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2103
            break;
2104
          }
2105
          case AnnotateDismissCommand:
2106
          {
2107
            /*
2108
              Prematurely exit.
2109
            */
2110
            state|=EscapeState;
2111
            state|=ExitState;
2112
            break;
2113
          }
2114
          default:
2115
            break;
2116
        }
2117
        continue;
2118
      }
2119
    switch (event.type)
2120
    {
2121
      case ButtonPress:
2122
      {
2123
        if (event.xbutton.button != Button1)
2124
          break;
2125
        if (event.xbutton.window != windows->image.id)
2126
          break;
2127
        /*
2128
          Change to text entering mode.
2129
        */
2130
        x=event.xbutton.x;
2131
        y=event.xbutton.y;
2132
        state|=ExitState;
2133
        break;
2134
      }
2135
      case ButtonRelease:
2136
        break;
2137
      case Expose:
2138
        break;
2139
      case KeyPress:
2140
      {
2141
        if (event.xkey.window != windows->image.id)
2142
          break;
2143
        /*
2144
          Respond to a user key press.
2145
        */
2146
        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2147
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2148
        switch ((int) key_symbol)
2149
        {
2150
          case XK_Escape:
2151
          case XK_F20:
2152
          {
2153
            /*
2154
              Prematurely exit.
2155
            */
2156
            state|=EscapeState;
2157
            state|=ExitState;
2158
            break;
2159
          }
2160
          case XK_F1:
2161
          case XK_Help:
2162
          {
2163
            XTextViewHelp(display,resource_info,windows,MagickFalse,
2164
              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2165
            break;
2166
          }
2167
          default:
2168
          {
2169
            (void) XBell(display,0);
2170
            break;
2171
          }
2172
        }
2173
        break;
2174
      }
2175
      case MotionNotify:
2176
      {
2177
        /*
2178
          Map and unmap Info widget as cursor crosses its boundaries.
2179
        */
2180
        x=event.xmotion.x;
2181
        y=event.xmotion.y;
2182
        if (windows->info.mapped != MagickFalse)
2183
          {
2184
            if ((x < (windows->info.x+(int) windows->info.width)) &&
2185
                (y < (windows->info.y+(int) windows->info.height)))
2186
              (void) XWithdrawWindow(display,windows->info.id,
2187
                windows->info.screen);
2188
          }
2189
        else
2190
          if ((x > (windows->info.x+(int) windows->info.width)) ||
2191
              (y > (windows->info.y+(int) windows->info.height)))
2192
            (void) XMapWindow(display,windows->info.id);
2193
        break;
2194
      }
2195
      default:
2196
        break;
2197
    }
2198
  } while ((state & ExitState) == 0);
2199
  (void) XSelectInput(display,windows->image.id,
2200
    windows->image.attributes.event_mask);
2201
  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2202
  if ((state & EscapeState) != 0)
2203
    return(MagickTrue);
2204
  /*
2205
    Set font info and check boundary conditions.
2206
  */
2207
  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2208
  if (font_info == (XFontStruct *) NULL)
2209
    {
2210
      XNoticeWidget(display,windows,"Unable to load font:",
2211
        resource_info->font_name[font_id]);
2212
      font_info=windows->font_info;
2213
    }
2214
  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2215
    x=(int) windows->image.width-font_info->max_bounds.width;
2216
  if (y < (int) (font_info->ascent+font_info->descent))
2217
    y=(int) font_info->ascent+font_info->descent;
2218
  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2219
      ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2220
    return(MagickFalse);
2221
  /*
2222
    Initialize annotate structure.
2223
  */
2224
  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2225
  if (annotate_info == (XAnnotateInfo *) NULL)
2226
    return(MagickFalse);
2227
  XGetAnnotateInfo(annotate_info);
2228
  annotate_info->x=x;
2229
  annotate_info->y=y;
2230
  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2231
    annotate_info->stencil=OpaqueStencil;
2232
  else
2233
    if (transparent_box == MagickFalse)
2234
      annotate_info->stencil=BackgroundStencil;
2235
    else
2236
      annotate_info->stencil=ForegroundStencil;
2237
  annotate_info->height=(unsigned int) (font_info->ascent+font_info->descent);
2238
  annotate_info->degrees=degrees;
2239
  annotate_info->font_info=font_info;
2240
  annotate_info->text=(char *) AcquireQuantumMemory((size_t) 
2241
    windows->image.width/(size_t) MagickMax(font_info->min_bounds.width,1)+2UL,
2242
    sizeof(*annotate_info->text));
2243
  if (annotate_info->text == (char *) NULL)
2244
    return(MagickFalse);
2245
  /*
2246
    Create cursor and set graphic context.
2247
  */
2248
  cursor=XCreateFontCursor(display,XC_pencil);
2249
  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2250
  annotate_context=windows->image.annotate_context;
2251
  (void) XSetFont(display,annotate_context,font_info->fid);
2252
  (void) XSetBackground(display,annotate_context,
2253
    windows->pixel_info->pen_colors[box_id].pixel);
2254
  (void) XSetForeground(display,annotate_context,
2255
    windows->pixel_info->pen_colors[pen_id].pixel);
2256
  /*
2257
    Begin annotating the image with text.
2258
  */
2259
  (void) CloneString(&windows->command.name,"Text");
2260
  windows->command.data=0;
2261
  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2262
  state=DefaultState;
2263
  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2264
  text_event.xexpose.width=(int) font_info->max_bounds.width;
2265
  text_event.xexpose.height=font_info->max_bounds.ascent+
2266
    font_info->max_bounds.descent;
2267
  p=annotate_info->text;
2268
  do
2269
  {
2270
    /*
2271
      Display text cursor.
2272
    */
2273
    *p='\0';
2274
    (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2275
    /*
2276
      Wait for next event.
2277
    */
2278
    XScreenEvent(display,windows,&event,exception);
2279
    if (event.xany.window == windows->command.id)
2280
      {
2281
        /*
2282
          Select a command from the Command widget.
2283
        */
2284
        (void) XSetBackground(display,annotate_context,
2285
          windows->pixel_info->background_color.pixel);
2286
        (void) XSetForeground(display,annotate_context,
2287
          windows->pixel_info->foreground_color.pixel);
2288
        id=XCommandWidget(display,windows,AnnotateMenu,&event);
2289
        (void) XSetBackground(display,annotate_context,
2290
          windows->pixel_info->pen_colors[box_id].pixel);
2291
        (void) XSetForeground(display,annotate_context,
2292
          windows->pixel_info->pen_colors[pen_id].pixel);
2293
        if (id < 0)
2294
          continue;
2295
        switch (TextCommands[id])
2296
        {
2297
          case TextHelpCommand:
2298
          {
2299
            XTextViewHelp(display,resource_info,windows,MagickFalse,
2300
              "Help Viewer - Image Annotation",ImageAnnotateHelp);
2301
            (void) XCheckDefineCursor(display,windows->image.id,cursor);
2302
            break;
2303
          }
2304
          case TextApplyCommand:
2305
          {
2306
            /*
2307
              Finished annotating.
2308
            */
2309
            annotate_info->width=(unsigned int) XTextWidth(font_info,
2310
              annotate_info->text,(int) strlen(annotate_info->text));
2311
            XRefreshWindow(display,&windows->image,&text_event);
2312
            state|=ExitState;
2313
            break;
2314
          }
2315
          default:
2316
            break;
2317
        }
2318
        continue;
2319
      }
2320
    /*
2321
      Erase text cursor.
2322
    */
2323
    text_event.xexpose.x=x;
2324
    text_event.xexpose.y=y-font_info->max_bounds.ascent;
2325
    (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2326
      (unsigned int) text_event.xexpose.width,(unsigned int)
2327
      text_event.xexpose.height,MagickFalse);
2328
    XRefreshWindow(display,&windows->image,&text_event);
2329
    switch (event.type)
2330
    {
2331
      case ButtonPress:
2332
      {
2333
        if (event.xbutton.window != windows->image.id)
2334
          break;
2335
        if (event.xbutton.button == Button2)
2336
          {
2337
            /*
2338
              Request primary selection.
2339
            */
2340
            (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2341
              windows->image.id,CurrentTime);
2342
            break;
2343
          }
2344
        break;
2345
      }
2346
      case Expose:
2347
      {
2348
        if (event.xexpose.count == 0)
2349
          {
2350
            XAnnotateInfo
2351
              *text_info;
2352
2353
            /*
2354
              Refresh Image window.
2355
            */
2356
            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2357
            text_info=annotate_info;
2358
            while (text_info != (XAnnotateInfo *) NULL)
2359
            {
2360
              if (annotate_info->stencil == ForegroundStencil)
2361
                (void) XDrawString(display,windows->image.id,annotate_context,
2362
                  text_info->x,text_info->y,text_info->text,
2363
                  (int) strlen(text_info->text));
2364
              else
2365
                (void) XDrawImageString(display,windows->image.id,
2366
                  annotate_context,text_info->x,text_info->y,text_info->text,
2367
                  (int) strlen(text_info->text));
2368
              text_info=text_info->previous;
2369
            }
2370
            (void) XDrawString(display,windows->image.id,annotate_context,
2371
              x,y,"_",1);
2372
          }
2373
        break;
2374
      }
2375
      case KeyPress:
2376
      {
2377
        int
2378
          length;
2379
2380
        if (event.xkey.window != windows->image.id)
2381
          break;
2382
        /*
2383
          Respond to a user key press.
2384
        */
2385
        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2386
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2387
        *(command+length)='\0';
2388
        if (((event.xkey.state & ControlMask) != 0) ||
2389
            ((event.xkey.state & Mod1Mask) != 0))
2390
          state|=ModifierState;
2391
        if ((state & ModifierState) != 0)
2392
          switch ((int) key_symbol)
2393
          {
2394
            case XK_u:
2395
            case XK_U:
2396
            {
2397
              key_symbol=DeleteCommand;
2398
              break;
2399
            }
2400
            default:
2401
              break;
2402
          }
2403
        switch ((int) key_symbol)
2404
        {
2405
          case XK_BackSpace:
2406
          {
2407
            /*
2408
              Erase one character.
2409
            */
2410
            if (p == annotate_info->text)
2411
              {
2412
                if (annotate_info->previous == (XAnnotateInfo *) NULL)
2413
                  break;
2414
                else
2415
                  {
2416
                    /*
2417
                      Go to end of the previous line of text.
2418
                    */
2419
                    annotate_info=annotate_info->previous;
2420
                    p=annotate_info->text;
2421
                    x=annotate_info->x+(int) annotate_info->width;
2422
                    y=annotate_info->y;
2423
                    if (annotate_info->width != 0)
2424
                      p+=(ptrdiff_t) strlen(annotate_info->text);
2425
                    break;
2426
                  }
2427
              }
2428
            p--;
2429
            x-=XTextWidth(font_info,p,1);
2430
            text_event.xexpose.x=x;
2431
            text_event.xexpose.y=y-font_info->max_bounds.ascent;
2432
            XRefreshWindow(display,&windows->image,&text_event);
2433
            break;
2434
          }
2435
          case XK_bracketleft:
2436
          {
2437
            key_symbol=XK_Escape;
2438
            break;
2439
          }
2440
          case DeleteCommand:
2441
          {
2442
            /*
2443
              Erase the entire line of text.
2444
            */
2445
            while (p != annotate_info->text)
2446
            {
2447
              p--;
2448
              x-=XTextWidth(font_info,p,1);
2449
              text_event.xexpose.x=x;
2450
              XRefreshWindow(display,&windows->image,&text_event);
2451
            }
2452
            break;
2453
          }
2454
          case XK_Escape:
2455
          case XK_F20:
2456
          {
2457
            /*
2458
              Finished annotating.
2459
            */
2460
            annotate_info->width=(unsigned int) XTextWidth(font_info,
2461
              annotate_info->text,(int) strlen(annotate_info->text));
2462
            XRefreshWindow(display,&windows->image,&text_event);
2463
            state|=ExitState;
2464
            break;
2465
          }
2466
          default:
2467
          {
2468
            /*
2469
              Draw a single character on the Image window.
2470
            */
2471
            if ((state & ModifierState) != 0)
2472
              break;
2473
            if (*command == '\0')
2474
              break;
2475
            *p=(*command);
2476
            if (annotate_info->stencil == ForegroundStencil)
2477
              (void) XDrawString(display,windows->image.id,annotate_context,
2478
                x,y,p,1);
2479
            else
2480
              (void) XDrawImageString(display,windows->image.id,
2481
                annotate_context,x,y,p,1);
2482
            x+=XTextWidth(font_info,p,1);
2483
            p++;
2484
            if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2485
              break;
2486
            magick_fallthrough;
2487
          }
2488
          case XK_Return:
2489
          case XK_KP_Enter:
2490
          {
2491
            /*
2492
              Advance to the next line of text.
2493
            */
2494
            *p='\0';
2495
            annotate_info->width=(unsigned int) XTextWidth(font_info,
2496
              annotate_info->text,(int) strlen(annotate_info->text));
2497
            if (annotate_info->next != (XAnnotateInfo *) NULL)
2498
              {
2499
                /*
2500
                  Line of text already exists.
2501
                */
2502
                annotate_info=annotate_info->next;
2503
                x=annotate_info->x;
2504
                y=annotate_info->y;
2505
                p=annotate_info->text;
2506
                break;
2507
              }
2508
            annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2509
              sizeof(*annotate_info->next));
2510
            if (annotate_info->next == (XAnnotateInfo *) NULL)
2511
              return(MagickFalse);
2512
            *annotate_info->next=(*annotate_info);
2513
            annotate_info->next->previous=annotate_info;
2514
            annotate_info=annotate_info->next;
2515
            annotate_info->text=(char *) AcquireQuantumMemory((size_t) (
2516
             (ssize_t)  windows->image.width/MagickMax((ssize_t)
2517
             font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2518
            if (annotate_info->text == (char *) NULL)
2519
              return(MagickFalse);
2520
            annotate_info->y+=(ssize_t) annotate_info->height;
2521
            if (annotate_info->y > (int) windows->image.height)
2522
              annotate_info->y=(int) annotate_info->height;
2523
            annotate_info->next=(XAnnotateInfo *) NULL;
2524
            x=annotate_info->x;
2525
            y=annotate_info->y;
2526
            p=annotate_info->text;
2527
            break;
2528
          }
2529
        }
2530
        break;
2531
      }
2532
      case KeyRelease:
2533
      {
2534
        /*
2535
          Respond to a user key release.
2536
        */
2537
        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2538
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2539
        state&=(size_t) (~ModifierState);
2540
        break;
2541
      }
2542
      case SelectionNotify:
2543
      {
2544
        Atom
2545
          type;
2546
2547
        int
2548
          format;
2549
2550
        unsigned char
2551
          *data;
2552
2553
        unsigned long
2554
          after,
2555
          length;
2556
2557
        /*
2558
          Obtain response from primary selection.
2559
        */
2560
        if (event.xselection.property == (Atom) None)
2561
          break;
2562
        status=XGetWindowProperty(display,event.xselection.requestor,
2563
          event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2564
          &type,&format,&length,&after,&data);
2565
        if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2566
            (length == 0))
2567
          break;
2568
        /*
2569
          Annotate Image window with primary selection.
2570
        */
2571
        for (i=0; i < (ssize_t) length; i++)
2572
        {
2573
          if ((char) data[i] != '\n')
2574
            {
2575
              /*
2576
                Draw a single character on the Image window.
2577
              */
2578
              *p=(char) data[i];
2579
              (void) XDrawString(display,windows->image.id,annotate_context,
2580
                x,y,p,1);
2581
              x+=XTextWidth(font_info,p,1);
2582
              p++;
2583
              if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2584
                continue;
2585
            }
2586
          /*
2587
            Advance to the next line of text.
2588
          */
2589
          *p='\0';
2590
          annotate_info->width=(unsigned int) XTextWidth(font_info,
2591
            annotate_info->text,(int) strlen(annotate_info->text));
2592
          if (annotate_info->next != (XAnnotateInfo *) NULL)
2593
            {
2594
              /*
2595
                Line of text already exists.
2596
              */
2597
              annotate_info=annotate_info->next;
2598
              x=annotate_info->x;
2599
              y=annotate_info->y;
2600
              p=annotate_info->text;
2601
              continue;
2602
            }
2603
          annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2604
            sizeof(*annotate_info->next));
2605
          if (annotate_info->next == (XAnnotateInfo *) NULL)
2606
            return(MagickFalse);
2607
          *annotate_info->next=(*annotate_info);
2608
          annotate_info->next->previous=annotate_info;
2609
          annotate_info=annotate_info->next;
2610
          annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2611
            (windows->image.width/MagickMax((ssize_t)
2612
            font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2613
          if (annotate_info->text == (char *) NULL)
2614
            return(MagickFalse);
2615
          annotate_info->y+=(ssize_t) annotate_info->height;
2616
          if (annotate_info->y > (int) windows->image.height)
2617
            annotate_info->y=(int) annotate_info->height;
2618
          annotate_info->next=(XAnnotateInfo *) NULL;
2619
          x=annotate_info->x;
2620
          y=annotate_info->y;
2621
          p=annotate_info->text;
2622
        }
2623
        (void) XFree((void *) data);
2624
        break;
2625
      }
2626
      default:
2627
        break;
2628
    }
2629
  } while ((state & ExitState) == 0);
2630
  (void) XFreeCursor(display,cursor);
2631
  /*
2632
    Annotation is relative to image configuration.
2633
  */
2634
  width=(unsigned int) image->columns;
2635
  height=(unsigned int) image->rows;
2636
  x=0;
2637
  y=0;
2638
  if (windows->image.crop_geometry != (char *) NULL)
2639
    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2640
  /*
2641
    Initialize annotated image.
2642
  */
2643
  XSetCursorState(display,windows,MagickTrue);
2644
  XCheckRefreshWindows(display,windows);
2645
  while (annotate_info != (XAnnotateInfo *) NULL)
2646
  {
2647
    if (annotate_info->width == 0)
2648
      {
2649
        /*
2650
          No text on this line--  go to the next line of text.
2651
        */
2652
        previous_info=annotate_info->previous;
2653
        annotate_info->text=(char *)
2654
          RelinquishMagickMemory(annotate_info->text);
2655
        annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2656
        annotate_info=previous_info;
2657
        continue;
2658
      }
2659
    /*
2660
      Determine pixel index for box and pen color.
2661
    */
2662
    windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2663
    if (windows->pixel_info->colors != 0)
2664
      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2665
        if (windows->pixel_info->pixels[i] ==
2666
            windows->pixel_info->pen_colors[box_id].pixel)
2667
          {
2668
            windows->pixel_info->box_index=(unsigned short) i;
2669
            break;
2670
          }
2671
    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2672
    if (windows->pixel_info->colors != 0)
2673
      for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2674
        if (windows->pixel_info->pixels[i] ==
2675
            windows->pixel_info->pen_colors[pen_id].pixel)
2676
          {
2677
            windows->pixel_info->pen_index=(unsigned short) i;
2678
            break;
2679
          }
2680
    /*
2681
      Define the annotate geometry string.
2682
    */
2683
    annotate_info->x=(int)
2684
      width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2685
    annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2686
      windows->image.y)/windows->image.ximage->height;
2687
    (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2688
      "%gx%g%+g%+g",(double) width*annotate_info->width/
2689
      windows->image.ximage->width,(double) height*annotate_info->height/
2690
      windows->image.ximage->height,(double) annotate_info->x+x,(double)
2691
      annotate_info->y+y);
2692
    /*
2693
      Annotate image with text.
2694
    */
2695
    status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2696
      exception) == MagickFalse ? 0 : 1;
2697
    if (status == 0)
2698
      return(MagickFalse);
2699
    /*
2700
      Free up memory.
2701
    */
2702
    previous_info=annotate_info->previous;
2703
    annotate_info->text=DestroyString(annotate_info->text);
2704
    annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2705
    annotate_info=previous_info;
2706
  }
2707
  (void) XSetForeground(display,annotate_context,
2708
    windows->pixel_info->foreground_color.pixel);
2709
  (void) XSetBackground(display,annotate_context,
2710
    windows->pixel_info->background_color.pixel);
2711
  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2712
  XSetCursorState(display,windows,MagickFalse);
2713
  (void) XFreeFont(display,font_info);
2714
  /*
2715
    Update image configuration.
2716
  */
2717
  XConfigureImageColormap(display,resource_info,windows,image,exception);
2718
  (void) XConfigureImage(display,resource_info,windows,image,exception);
2719
  return(MagickTrue);
2720
}
2721

2722
/*
2723
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2724
%                                                                             %
2725
%                                                                             %
2726
%                                                                             %
2727
+   X B a c k g r o u n d I m a g e                                           %
2728
%                                                                             %
2729
%                                                                             %
2730
%                                                                             %
2731
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2732
%
2733
%  XBackgroundImage() displays the image in the background of a window.
2734
%
2735
%  The format of the XBackgroundImage method is:
2736
%
2737
%      MagickBooleanType XBackgroundImage(Display *display,
2738
%        XResourceInfo *resource_info,XWindows *windows,Image **image,
2739
%        ExceptionInfo *exception)
2740
%
2741
%  A description of each parameter follows:
2742
%
2743
%    o display: Specifies a connection to an X server; returned from
2744
%      XOpenDisplay.
2745
%
2746
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2747
%
2748
%    o windows: Specifies a pointer to a XWindows structure.
2749
%
2750
%    o image: the image.
2751
%
2752
%    o exception: return any errors or warnings in this structure.
2753
%
2754
*/
2755
static MagickBooleanType XBackgroundImage(Display *display,
2756
  XResourceInfo *resource_info,XWindows *windows,Image **image,
2757
  ExceptionInfo *exception)
2758
{
2759
#define BackgroundImageTag  "Background/Image"
2760
2761
  int
2762
    status;
2763
2764
  static char
2765
    window_id[MagickPathExtent] = "root";
2766
2767
  XResourceInfo
2768
    background_resources;
2769
2770
  /*
2771
    Put image in background.
2772
  */
2773
  status=XDialogWidget(display,windows,"Background",
2774
    "Enter window id (id 0x00 selects window with pointer):",window_id);
2775
  if (*window_id == '\0')
2776
    return(MagickFalse);
2777
  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2778
    exception);
2779
  XInfoWidget(display,windows,BackgroundImageTag);
2780
  XSetCursorState(display,windows,MagickTrue);
2781
  XCheckRefreshWindows(display,windows);
2782
  background_resources=(*resource_info);
2783
  background_resources.window_id=window_id;
2784
  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2785
  status=XDisplayBackgroundImage(display,&background_resources,*image,
2786
    exception) == MagickFalse ? 0 : 1;
2787
  if (status != MagickFalse)
2788
    XClientMessage(display,windows->image.id,windows->im_protocols,
2789
      windows->im_retain_colors,CurrentTime);
2790
  XSetCursorState(display,windows,MagickFalse);
2791
  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2792
    exception);
2793
  return(MagickTrue);
2794
}
2795

2796
/*
2797
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2798
%                                                                             %
2799
%                                                                             %
2800
%                                                                             %
2801
+   X C h o p I m a g e                                                       %
2802
%                                                                             %
2803
%                                                                             %
2804
%                                                                             %
2805
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2806
%
2807
%  XChopImage() chops the X image.
2808
%
2809
%  The format of the XChopImage method is:
2810
%
2811
%    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2812
%      XWindows *windows,Image **image,ExceptionInfo *exception)
2813
%
2814
%  A description of each parameter follows:
2815
%
2816
%    o display: Specifies a connection to an X server; returned from
2817
%      XOpenDisplay.
2818
%
2819
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2820
%
2821
%    o windows: Specifies a pointer to a XWindows structure.
2822
%
2823
%    o image: the image.
2824
%
2825
%    o exception: return any errors or warnings in this structure.
2826
%
2827
*/
2828
static MagickBooleanType XChopImage(Display *display,
2829
  XResourceInfo *resource_info,XWindows *windows,Image **image,
2830
  ExceptionInfo *exception)
2831
{
2832
  const char
2833
    *const ChopMenu[] =
2834
    {
2835
      "Direction",
2836
      "Help",
2837
      "Dismiss",
2838
      (char *) NULL
2839
    };
2840
2841
  static ModeType
2842
    direction = HorizontalChopCommand;
2843
2844
  static const ModeType
2845
    ChopCommands[] =
2846
    {
2847
      ChopDirectionCommand,
2848
      ChopHelpCommand,
2849
      ChopDismissCommand
2850
    },
2851
    DirectionCommands[] =
2852
    {
2853
      HorizontalChopCommand,
2854
      VerticalChopCommand
2855
    };
2856
2857
  char
2858
    text[MagickPathExtent];
2859
2860
  double
2861
    scale_factor;
2862
2863
  Image
2864
    *chop_image;
2865
2866
  int
2867
    id,
2868
    x,
2869
    y;
2870
2871
  RectangleInfo
2872
    chop_info;
2873
2874
  size_t
2875
    state;
2876
2877
  unsigned int
2878
    distance,
2879
    height,
2880
    width;
2881
2882
  XEvent
2883
    event;
2884
2885
  XSegment
2886
    segment_info;
2887
2888
  /*
2889
    Map Command widget.
2890
  */
2891
  (void) CloneString(&windows->command.name,"Chop");
2892
  windows->command.data=1;
2893
  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2894
  (void) XMapRaised(display,windows->command.id);
2895
  XClientMessage(display,windows->image.id,windows->im_protocols,
2896
    windows->im_update_widget,CurrentTime);
2897
  /*
2898
    Track pointer until button 1 is pressed.
2899
  */
2900
  XQueryPosition(display,windows->image.id,&x,&y);
2901
  (void) XSelectInput(display,windows->image.id,
2902
    windows->image.attributes.event_mask | PointerMotionMask);
2903
  state=DefaultState;
2904
  (void) memset(&segment_info,0,sizeof(segment_info));
2905
  do
2906
  {
2907
    if (windows->info.mapped != MagickFalse)
2908
      {
2909
        /*
2910
          Display pointer position.
2911
        */
2912
        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2913
          x+windows->image.x,y+windows->image.y);
2914
        XInfoWidget(display,windows,text);
2915
      }
2916
    /*
2917
      Wait for next event.
2918
    */
2919
    XScreenEvent(display,windows,&event,exception);
2920
    if (event.xany.window == windows->command.id)
2921
      {
2922
        /*
2923
          Select a command from the Command widget.
2924
        */
2925
        id=XCommandWidget(display,windows,ChopMenu,&event);
2926
        if (id < 0)
2927
          continue;
2928
        switch (ChopCommands[id])
2929
        {
2930
          case ChopDirectionCommand:
2931
          {
2932
            char
2933
              command[MagickPathExtent];
2934
2935
            const char
2936
              *const Directions[] =
2937
              {
2938
                "horizontal",
2939
                "vertical",
2940
                (char *) NULL,
2941
              };
2942
2943
            /*
2944
              Select a command from the pop-up menu.
2945
            */
2946
            id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2947
            if (id >= 0)
2948
              direction=DirectionCommands[id];
2949
            break;
2950
          }
2951
          case ChopHelpCommand:
2952
          {
2953
            XTextViewHelp(display,resource_info,windows,MagickFalse,
2954
              "Help Viewer - Image Chop",ImageChopHelp);
2955
            break;
2956
          }
2957
          case ChopDismissCommand:
2958
          {
2959
            /*
2960
              Prematurely exit.
2961
            */
2962
            state|=EscapeState;
2963
            state|=ExitState;
2964
            break;
2965
          }
2966
          default:
2967
            break;
2968
        }
2969
        continue;
2970
      }
2971
    switch (event.type)
2972
    {
2973
      case ButtonPress:
2974
      {
2975
        if (event.xbutton.button != Button1)
2976
          break;
2977
        if (event.xbutton.window != windows->image.id)
2978
          break;
2979
        /*
2980
          User has committed to start point of chopping line.
2981
        */
2982
        segment_info.x1=(short int) event.xbutton.x;
2983
        segment_info.x2=(short int) event.xbutton.x;
2984
        segment_info.y1=(short int) event.xbutton.y;
2985
        segment_info.y2=(short int) event.xbutton.y;
2986
        state|=ExitState;
2987
        break;
2988
      }
2989
      case ButtonRelease:
2990
        break;
2991
      case Expose:
2992
        break;
2993
      case KeyPress:
2994
      {
2995
        char
2996
          command[MagickPathExtent];
2997
2998
        KeySym
2999
          key_symbol;
3000
3001
        if (event.xkey.window != windows->image.id)
3002
          break;
3003
        /*
3004
          Respond to a user key press.
3005
        */
3006
        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3007
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3008
        switch ((int) key_symbol)
3009
        {
3010
          case XK_Escape:
3011
          case XK_F20:
3012
          {
3013
            /*
3014
              Prematurely exit.
3015
            */
3016
            state|=EscapeState;
3017
            state|=ExitState;
3018
            break;
3019
          }
3020
          case XK_F1:
3021
          case XK_Help:
3022
          {
3023
            (void) XSetFunction(display,windows->image.highlight_context,
3024
              GXcopy);
3025
            XTextViewHelp(display,resource_info,windows,MagickFalse,
3026
              "Help Viewer - Image Chop",ImageChopHelp);
3027
            (void) XSetFunction(display,windows->image.highlight_context,
3028
              GXinvert);
3029
            break;
3030
          }
3031
          default:
3032
          {
3033
            (void) XBell(display,0);
3034
            break;
3035
          }
3036
        }
3037
        break;
3038
      }
3039
      case MotionNotify:
3040
      {
3041
        /*
3042
          Map and unmap Info widget as text cursor crosses its boundaries.
3043
        */
3044
        x=event.xmotion.x;
3045
        y=event.xmotion.y;
3046
        if (windows->info.mapped != MagickFalse)
3047
          {
3048
            if ((x < (windows->info.x+(int) windows->info.width)) &&
3049
                (y < (windows->info.y+(int) windows->info.height)))
3050
              (void) XWithdrawWindow(display,windows->info.id,
3051
                windows->info.screen);
3052
          }
3053
        else
3054
          if ((x > (windows->info.x+(int) windows->info.width)) ||
3055
              (y > (windows->info.y+(int) windows->info.height)))
3056
            (void) XMapWindow(display,windows->info.id);
3057
      }
3058
    }
3059
  } while ((state & ExitState) == 0);
3060
  (void) XSelectInput(display,windows->image.id,
3061
    windows->image.attributes.event_mask);
3062
  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3063
  if ((state & EscapeState) != 0)
3064
    return(MagickTrue);
3065
  /*
3066
    Draw line as pointer moves until the mouse button is released.
3067
  */
3068
  chop_info.width=0;
3069
  chop_info.height=0;
3070
  chop_info.x=0;
3071
  chop_info.y=0;
3072
  distance=0;
3073
  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3074
  state=DefaultState;
3075
  do
3076
  {
3077
    if (distance > 9)
3078
      {
3079
        /*
3080
          Display info and draw chopping line.
3081
        */
3082
        if (windows->info.mapped == MagickFalse)
3083
          (void) XMapWindow(display,windows->info.id);
3084
        (void) FormatLocaleString(text,MagickPathExtent,
3085
          " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3086
          chop_info.height,(double) chop_info.x,(double) chop_info.y);
3087
        XInfoWidget(display,windows,text);
3088
        XHighlightLine(display,windows->image.id,
3089
          windows->image.highlight_context,&segment_info);
3090
      }
3091
    else
3092
      if (windows->info.mapped != MagickFalse)
3093
        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3094
    /*
3095
      Wait for next event.
3096
    */
3097
    XScreenEvent(display,windows,&event,exception);
3098
    if (distance > 9)
3099
      XHighlightLine(display,windows->image.id,
3100
        windows->image.highlight_context,&segment_info);
3101
    switch (event.type)
3102
    {
3103
      case ButtonPress:
3104
      {
3105
        segment_info.x2=(short int) event.xmotion.x;
3106
        segment_info.y2=(short int) event.xmotion.y;
3107
        break;
3108
      }
3109
      case ButtonRelease:
3110
      {
3111
        /*
3112
          User has committed to chopping line.
3113
        */
3114
        segment_info.x2=(short int) event.xbutton.x;
3115
        segment_info.y2=(short int) event.xbutton.y;
3116
        state|=ExitState;
3117
        break;
3118
      }
3119
      case Expose:
3120
        break;
3121
      case MotionNotify:
3122
      {
3123
        segment_info.x2=(short int) event.xmotion.x;
3124
        segment_info.y2=(short int) event.xmotion.y;
3125
      }
3126
      default:
3127
        break;
3128
    }
3129
    /*
3130
      Check boundary conditions.
3131
    */
3132
    if (segment_info.x2 < 0)
3133
      segment_info.x2=0;
3134
    else
3135
      if (segment_info.x2 > windows->image.ximage->width)
3136
        segment_info.x2=windows->image.ximage->width;
3137
    if (segment_info.y2 < 0)
3138
      segment_info.y2=0;
3139
    else
3140
      if (segment_info.y2 > windows->image.ximage->height)
3141
        segment_info.y2=windows->image.ximage->height;
3142
    distance=(unsigned int)
3143
      (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3144
       ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3145
    /*
3146
      Compute chopping geometry.
3147
    */
3148
    if (direction == HorizontalChopCommand)
3149
      {
3150
        chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3151
        chop_info.height=0;
3152
        chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3153
        chop_info.y=0;
3154
        if (segment_info.x1 > segment_info.x2)
3155
          {
3156
            chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3157
            chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3158
          }
3159
      }
3160
    else
3161
      {
3162
        chop_info.width=0;
3163
        chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3164
        chop_info.x=0;
3165
        chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3166
        if (segment_info.y1 > segment_info.y2)
3167
          {
3168
            chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3169
            chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3170
          }
3171
      }
3172
  } while ((state & ExitState) == 0);
3173
  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3174
  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3175
  if (distance <= 9)
3176
    return(MagickTrue);
3177
  /*
3178
    Image chopping is relative to image configuration.
3179
  */
3180
  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3181
    exception);
3182
  XSetCursorState(display,windows,MagickTrue);
3183
  XCheckRefreshWindows(display,windows);
3184
  windows->image.window_changes.width=windows->image.ximage->width-
3185
    (int) chop_info.width;
3186
  windows->image.window_changes.height=windows->image.ximage->height-
3187
    (int) chop_info.height;
3188
  width=(unsigned int) (*image)->columns;
3189
  height=(unsigned int) (*image)->rows;
3190
  x=0;
3191
  y=0;
3192
  if (windows->image.crop_geometry != (char *) NULL)
3193
    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3194
  scale_factor=(double) width/windows->image.ximage->width;
3195
  chop_info.x+=x;
3196
  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3197
  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3198
  scale_factor=(double) height/windows->image.ximage->height;
3199
  chop_info.y+=y;
3200
  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3201
  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3202
  /*
3203
    Chop image.
3204
  */
3205
  chop_image=ChopImage(*image,&chop_info,exception);
3206
  XSetCursorState(display,windows,MagickFalse);
3207
  if (chop_image == (Image *) NULL)
3208
    return(MagickFalse);
3209
  *image=DestroyImage(*image);
3210
  *image=chop_image;
3211
  /*
3212
    Update image configuration.
3213
  */
3214
  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3215
  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3216
  return(MagickTrue);
3217
}
3218

3219
/*
3220
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221
%                                                                             %
3222
%                                                                             %
3223
%                                                                             %
3224
+   X C o l o r E d i t I m a g e                                             %
3225
%                                                                             %
3226
%                                                                             %
3227
%                                                                             %
3228
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3229
%
3230
%  XColorEditImage() allows the user to interactively change the color of one
3231
%  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3232
%
3233
%  The format of the XColorEditImage method is:
3234
%
3235
%      MagickBooleanType XColorEditImage(Display *display,
3236
%        XResourceInfo *resource_info,XWindows *windows,Image **image,
3237
%          ExceptionInfo *exception)
3238
%
3239
%  A description of each parameter follows:
3240
%
3241
%    o display: Specifies a connection to an X server;  returned from
3242
%      XOpenDisplay.
3243
%
3244
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3245
%
3246
%    o windows: Specifies a pointer to a XWindows structure.
3247
%
3248
%    o image: the image; returned from ReadImage.
3249
%
3250
%    o exception: return any errors or warnings in this structure.
3251
%
3252
*/
3253
static MagickBooleanType XColorEditImage(Display *display,
3254
  XResourceInfo *resource_info,XWindows *windows,Image **image,
3255
  ExceptionInfo *exception)
3256
{
3257
  const char
3258
    *const ColorEditMenu[] =
3259
    {
3260
      "Method",
3261
      "Pixel Color",
3262
      "Border Color",
3263
      "Fuzz",
3264
      "Undo",
3265
      "Help",
3266
      "Dismiss",
3267
      (char *) NULL
3268
    };
3269
3270
  static const ModeType
3271
    ColorEditCommands[] =
3272
    {
3273
      ColorEditMethodCommand,
3274
      ColorEditColorCommand,
3275
      ColorEditBorderCommand,
3276
      ColorEditFuzzCommand,
3277
      ColorEditUndoCommand,
3278
      ColorEditHelpCommand,
3279
      ColorEditDismissCommand
3280
    };
3281
3282
  static PaintMethod
3283
    method = PointMethod;
3284
3285
  static unsigned int
3286
    pen_id = 0;
3287
3288
  static XColor
3289
    border_color = { 0, 0, 0, 0, 0, 0 };
3290
3291
  char
3292
    command[MagickPathExtent],
3293
    text[MagickPathExtent];
3294
3295
  Cursor
3296
    cursor;
3297
3298
  int
3299
    entry,
3300
    id,
3301
    x,
3302
    x_offset,
3303
    y,
3304
    y_offset;
3305
3306
  Quantum
3307
    *q;
3308
3309
  size_t
3310
    state;
3311
3312
  ssize_t
3313
    i;
3314
3315
  unsigned int
3316
    height,
3317
    width;
3318
3319
  XColor
3320
    color;
3321
3322
  XEvent
3323
    event;
3324
3325
  /*
3326
    Map Command widget.
3327
  */
3328
  (void) CloneString(&windows->command.name,"Color Edit");
3329
  windows->command.data=4;
3330
  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3331
  (void) XMapRaised(display,windows->command.id);
3332
  XClientMessage(display,windows->image.id,windows->im_protocols,
3333
    windows->im_update_widget,CurrentTime);
3334
  /*
3335
    Make cursor.
3336
  */
3337
  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3338
    resource_info->background_color,resource_info->foreground_color);
3339
  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3340
  /*
3341
    Track pointer until button 1 is pressed.
3342
  */
3343
  XQueryPosition(display,windows->image.id,&x,&y);
3344
  (void) XSelectInput(display,windows->image.id,
3345
    windows->image.attributes.event_mask | PointerMotionMask);
3346
  state=DefaultState;
3347
  do
3348
  {
3349
    if (windows->info.mapped != MagickFalse)
3350
      {
3351
        /*
3352
          Display pointer position.
3353
        */
3354
        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3355
          x+windows->image.x,y+windows->image.y);
3356
        XInfoWidget(display,windows,text);
3357
      }
3358
    /*
3359
      Wait for next event.
3360
    */
3361
    XScreenEvent(display,windows,&event,exception);
3362
    if (event.xany.window == windows->command.id)
3363
      {
3364
        /*
3365
          Select a command from the Command widget.
3366
        */
3367
        id=XCommandWidget(display,windows,ColorEditMenu,&event);
3368
        if (id < 0)
3369
          {
3370
            (void) XCheckDefineCursor(display,windows->image.id,cursor);
3371
            continue;
3372
          }
3373
        switch (ColorEditCommands[id])
3374
        {
3375
          case ColorEditMethodCommand:
3376
          {
3377
            char
3378
              **methods;
3379
3380
            /*
3381
              Select a method from the pop-up menu.
3382
            */
3383
            methods=(char **) GetCommandOptions(MagickMethodOptions);
3384
            if (methods == (char **) NULL)
3385
              break;
3386
            entry=XMenuWidget(display,windows,ColorEditMenu[id],
3387
              (const char **) methods,command);
3388
            if (entry >= 0)
3389
              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3390
                MagickFalse,methods[entry]);
3391
            methods=DestroyStringList(methods);
3392
            break;
3393
          }
3394
          case ColorEditColorCommand:
3395
          {
3396
            const char
3397
              *ColorMenu[MaxNumberPens];
3398
3399
            int
3400
              pen_number;
3401
3402
            /*
3403
              Initialize menu selections.
3404
            */
3405
            for (i=0; i < (int) (MaxNumberPens-2); i++)
3406
              ColorMenu[i]=resource_info->pen_colors[i];
3407
            ColorMenu[MaxNumberPens-2]="Browser...";
3408
            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3409
            /*
3410
              Select a pen color from the pop-up menu.
3411
            */
3412
            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3413
              (const char **) ColorMenu,command);
3414
            if (pen_number < 0)
3415
              break;
3416
            if (pen_number == (MaxNumberPens-2))
3417
              {
3418
                static char
3419
                  color_name[MagickPathExtent] = "gray";
3420
3421
                /*
3422
                  Select a pen color from a dialog.
3423
                */
3424
                resource_info->pen_colors[pen_number]=color_name;
3425
                XColorBrowserWidget(display,windows,"Select",color_name);
3426
                if (*color_name == '\0')
3427
                  break;
3428
              }
3429
            /*
3430
              Set pen color.
3431
            */
3432
            (void) XParseColor(display,windows->map_info->colormap,
3433
              resource_info->pen_colors[pen_number],&color);
3434
            XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3435
              (unsigned int) MaxColors,&color);
3436
            windows->pixel_info->pen_colors[pen_number]=color;
3437
            pen_id=(unsigned int) pen_number;
3438
            break;
3439
          }
3440
          case ColorEditBorderCommand:
3441
          {
3442
            const char
3443
              *ColorMenu[MaxNumberPens];
3444
3445
            int
3446
              pen_number;
3447
3448
            /*
3449
              Initialize menu selections.
3450
            */
3451
            for (i=0; i < (int) (MaxNumberPens-2); i++)
3452
              ColorMenu[i]=resource_info->pen_colors[i];
3453
            ColorMenu[MaxNumberPens-2]="Browser...";
3454
            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3455
            /*
3456
              Select a pen color from the pop-up menu.
3457
            */
3458
            pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3459
              (const char **) ColorMenu,command);
3460
            if (pen_number < 0)
3461
              break;
3462
            if (pen_number == (MaxNumberPens-2))
3463
              {
3464
                static char
3465
                  color_name[MagickPathExtent] = "gray";
3466
3467
                /*
3468
                  Select a pen color from a dialog.
3469
                */
3470
                resource_info->pen_colors[pen_number]=color_name;
3471
                XColorBrowserWidget(display,windows,"Select",color_name);
3472
                if (*color_name == '\0')
3473
                  break;
3474
              }
3475
            /*
3476
              Set border color.
3477
            */
3478
            (void) XParseColor(display,windows->map_info->colormap,
3479
              resource_info->pen_colors[pen_number],&border_color);
3480
            break;
3481
          }
3482
          case ColorEditFuzzCommand:
3483
          {
3484
            const char
3485
              *const FuzzMenu[] =
3486
              {
3487
                "0%",
3488
                "2%",
3489
                "5%",
3490
                "10%",
3491
                "15%",
3492
                "Dialog...",
3493
                (char *) NULL,
3494
              };
3495
3496
            static char
3497
              fuzz[MagickPathExtent];
3498
3499
            /*
3500
              Select a command from the pop-up menu.
3501
            */
3502
            entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3503
              command);
3504
            if (entry < 0)
3505
              break;
3506
            if (entry != 5)
3507
              {
3508
                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3509
                  QuantumRange+1.0);
3510
                break;
3511
              }
3512
            (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3513
            (void) XDialogWidget(display,windows,"Ok",
3514
              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3515
            if (*fuzz == '\0')
3516
              break;
3517
            (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3518
            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3519
              1.0);
3520
            break;
3521
          }
3522
          case ColorEditUndoCommand:
3523
          {
3524
            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3525
              image,exception);
3526
            break;
3527
          }
3528
          case ColorEditHelpCommand:
3529
          default:
3530
          {
3531
            XTextViewHelp(display,resource_info,windows,MagickFalse,
3532
              "Help Viewer - Image Annotation",ImageColorEditHelp);
3533
            break;
3534
          }
3535
          case ColorEditDismissCommand:
3536
          {
3537
            /*
3538
              Prematurely exit.
3539
            */
3540
            state|=EscapeState;
3541
            state|=ExitState;
3542
            break;
3543
          }
3544
        }
3545
        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3546
        continue;
3547
      }
3548
    switch (event.type)
3549
    {
3550
      case ButtonPress:
3551
      {
3552
        if (event.xbutton.button != Button1)
3553
          break;
3554
        if ((event.xbutton.window != windows->image.id) &&
3555
            (event.xbutton.window != windows->magnify.id))
3556
          break;
3557
        /*
3558
          exit loop.
3559
        */
3560
        x=event.xbutton.x;
3561
        y=event.xbutton.y;
3562
        (void) XMagickCommand(display,resource_info,windows,
3563
          SaveToUndoBufferCommand,image,exception);
3564
        state|=UpdateConfigurationState;
3565
        break;
3566
      }
3567
      case ButtonRelease:
3568
      {
3569
        if (event.xbutton.button != Button1)
3570
          break;
3571
        if ((event.xbutton.window != windows->image.id) &&
3572
            (event.xbutton.window != windows->magnify.id))
3573
          break;
3574
        /*
3575
          Update colormap information.
3576
        */
3577
        x=event.xbutton.x;
3578
        y=event.xbutton.y;
3579
        XConfigureImageColormap(display,resource_info,windows,*image,exception);
3580
        (void) XConfigureImage(display,resource_info,windows,*image,exception);
3581
        XInfoWidget(display,windows,text);
3582
        (void) XCheckDefineCursor(display,windows->image.id,cursor);
3583
        state&=(size_t) (~UpdateConfigurationState);
3584
        break;
3585
      }
3586
      case Expose:
3587
        break;
3588
      case KeyPress:
3589
      {
3590
        KeySym
3591
          key_symbol;
3592
3593
        if (event.xkey.window == windows->magnify.id)
3594
          {
3595
            Window
3596
              window;
3597
3598
            window=windows->magnify.id;
3599
            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3600
          }
3601
        if (event.xkey.window != windows->image.id)
3602
          break;
3603
        /*
3604
          Respond to a user key press.
3605
        */
3606
        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3607
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3608
        switch ((int) key_symbol)
3609
        {
3610
          case XK_Escape:
3611
          case XK_F20:
3612
          {
3613
            /*
3614
              Prematurely exit.
3615
            */
3616
            state|=ExitState;
3617
            break;
3618
          }
3619
          case XK_F1:
3620
          case XK_Help:
3621
          {
3622
            XTextViewHelp(display,resource_info,windows,MagickFalse,
3623
              "Help Viewer - Image Annotation",ImageColorEditHelp);
3624
            break;
3625
          }
3626
          default:
3627
          {
3628
            (void) XBell(display,0);
3629
            break;
3630
          }
3631
        }
3632
        break;
3633
      }
3634
      case MotionNotify:
3635
      {
3636
        /*
3637
          Map and unmap Info widget as cursor crosses its boundaries.
3638
        */
3639
        x=event.xmotion.x;
3640
        y=event.xmotion.y;
3641
        if (windows->info.mapped != MagickFalse)
3642
          {
3643
            if ((x < (windows->info.x+(int) windows->info.width)) &&
3644
                (y < (windows->info.y+(int) windows->info.height)))
3645
              (void) XWithdrawWindow(display,windows->info.id,
3646
                windows->info.screen);
3647
          }
3648
        else
3649
          if ((x > (windows->info.x+(int) windows->info.width)) ||
3650
              (y > (windows->info.y+(int) windows->info.height)))
3651
            (void) XMapWindow(display,windows->info.id);
3652
        break;
3653
      }
3654
      default:
3655
        break;
3656
    }
3657
    if (event.xany.window == windows->magnify.id)
3658
      {
3659
        x=windows->magnify.x-windows->image.x;
3660
        y=windows->magnify.y-windows->image.y;
3661
      }
3662
    x_offset=x;
3663
    y_offset=y;
3664
    if ((state & UpdateConfigurationState) != 0)
3665
      {
3666
        CacheView
3667
          *image_view;
3668
3669
        int
3670
          x,
3671
          y;
3672
3673
        /*
3674
          Pixel edit is relative to image configuration.
3675
        */
3676
        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3677
          MagickTrue);
3678
        color=windows->pixel_info->pen_colors[pen_id];
3679
        XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3680
        width=(unsigned int) (*image)->columns;
3681
        height=(unsigned int) (*image)->rows;
3682
        x=0;
3683
        y=0;
3684
        if (windows->image.crop_geometry != (char *) NULL)
3685
          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3686
            &width,&height);
3687
        x_offset=((int) width*(windows->image.x+x_offset)/(int)
3688
          windows->image.ximage->width+x);
3689
        y_offset=((int) height*(windows->image.y+y_offset)/(int)
3690
          windows->image.ximage->height+y);
3691
        if ((x_offset < 0) || (y_offset < 0))
3692
          continue;
3693
        if ((x_offset >= (int) (*image)->columns) ||
3694
            (y_offset >= (int) (*image)->rows))
3695
          continue;
3696
        image_view=AcquireAuthenticCacheView(*image,exception);
3697
        switch (method)
3698
        {
3699
          case PointMethod:
3700
          default:
3701
          {
3702
            /*
3703
              Update color information using point algorithm.
3704
            */
3705
            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3706
              return(MagickFalse);
3707
            q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3708
              (ssize_t) y_offset,1,1,exception);
3709
            if (q == (Quantum *) NULL)
3710
              break;
3711
            SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3712
            SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3713
            SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3714
            (void) SyncCacheViewAuthenticPixels(image_view,exception);
3715
            break;
3716
          }
3717
          case ReplaceMethod:
3718
          {
3719
            PixelInfo
3720
              pixel,
3721
              target;
3722
3723
            /*
3724
              Update color information using replace algorithm.
3725
            */
3726
            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3727
              x_offset,(ssize_t) y_offset,&target,exception);
3728
            if ((*image)->storage_class == DirectClass)
3729
              {
3730
                for (y=0; y < (int) (*image)->rows; y++)
3731
                {
3732
                  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3733
                    (*image)->columns,1,exception);
3734
                  if (q == (Quantum *) NULL)
3735
                    break;
3736
                  for (x=0; x < (int) (*image)->columns; x++)
3737
                  {
3738
                    GetPixelInfoPixel(*image,q,&pixel);
3739
                    if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3740
                      {
3741
                        SetPixelRed(*image,ScaleShortToQuantum(
3742
                          color.red),q);
3743
                        SetPixelGreen(*image,ScaleShortToQuantum(
3744
                          color.green),q);
3745
                        SetPixelBlue(*image,ScaleShortToQuantum(
3746
                          color.blue),q);
3747
                      }
3748
                    q+=(ptrdiff_t) GetPixelChannels(*image);
3749
                  }
3750
                  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3751
                    break;
3752
                }
3753
              }
3754
            else
3755
              {
3756
                for (i=0; i < (ssize_t) (*image)->colors; i++)
3757
                  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3758
                    {
3759
                      (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3760
                        color.red);
3761
                      (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3762
                        color.green);
3763
                      (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3764
                        color.blue);
3765
                    }
3766
                (void) SyncImage(*image,exception);
3767
              }
3768
            break;
3769
          }
3770
          case FloodfillMethod:
3771
          case FillToBorderMethod:
3772
          {
3773
            DrawInfo
3774
              *draw_info;
3775
3776
            PixelInfo
3777
              target;
3778
3779
            /*
3780
              Update color information using floodfill algorithm.
3781
            */
3782
            (void) GetOneVirtualPixelInfo(*image,
3783
              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3784
              y_offset,&target,exception);
3785
            if (method == FillToBorderMethod)
3786
              {
3787
                target.red=(double)
3788
                  ScaleShortToQuantum(border_color.red);
3789
                target.green=(double)
3790
                  ScaleShortToQuantum(border_color.green);
3791
                target.blue=(double)
3792
                  ScaleShortToQuantum(border_color.blue);
3793
              }
3794
            draw_info=CloneDrawInfo(resource_info->image_info,
3795
              (DrawInfo *) NULL);
3796
            (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3797
              AllCompliance,&draw_info->fill,exception);
3798
            (void) FloodfillPaintImage(*image,draw_info,&target,
3799
              (ssize_t)x_offset,(ssize_t)y_offset,
3800
              method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3801
            draw_info=DestroyDrawInfo(draw_info);
3802
            break;
3803
          }
3804
          case ResetMethod:
3805
          {
3806
            /*
3807
              Update color information using reset algorithm.
3808
            */
3809
            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3810
              return(MagickFalse);
3811
            for (y=0; y < (int) (*image)->rows; y++)
3812
            {
3813
              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3814
                (*image)->columns,1,exception);
3815
              if (q == (Quantum *) NULL)
3816
                break;
3817
              for (x=0; x < (int) (*image)->columns; x++)
3818
              {
3819
                SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3820
                SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3821
                SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3822
                q+=(ptrdiff_t) GetPixelChannels(*image);
3823
              }
3824
              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3825
                break;
3826
            }
3827
            break;
3828
          }
3829
        }
3830
        image_view=DestroyCacheView(image_view);
3831
        state&=(~0x0080U);
3832
      }
3833
  } while ((state & ExitState) == 0);
3834
  (void) XSelectInput(display,windows->image.id,
3835
    windows->image.attributes.event_mask);
3836
  XSetCursorState(display,windows,MagickFalse);
3837
  (void) XFreeCursor(display,cursor);
3838
  return(MagickTrue);
3839
}
3840

3841
/*
3842
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3843
%                                                                             %
3844
%                                                                             %
3845
%                                                                             %
3846
+   X C o m p o s i t e I m a g e                                             %
3847
%                                                                             %
3848
%                                                                             %
3849
%                                                                             %
3850
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3851
%
3852
%  XCompositeImage() requests an image name from the user, reads the image and
3853
%  composites it with the X window image at a location the user chooses with
3854
%  the pointer.
3855
%
3856
%  The format of the XCompositeImage method is:
3857
%
3858
%      MagickBooleanType XCompositeImage(Display *display,
3859
%        XResourceInfo *resource_info,XWindows *windows,Image *image,
3860
%        ExceptionInfo *exception)
3861
%
3862
%  A description of each parameter follows:
3863
%
3864
%    o display: Specifies a connection to an X server;  returned from
3865
%      XOpenDisplay.
3866
%
3867
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3868
%
3869
%    o windows: Specifies a pointer to a XWindows structure.
3870
%
3871
%    o image: the image; returned from ReadImage.
3872
%
3873
%    o exception: return any errors or warnings in this structure.
3874
%
3875
*/
3876
static MagickBooleanType XCompositeImage(Display *display,
3877
  XResourceInfo *resource_info,XWindows *windows,Image *image,
3878
  ExceptionInfo *exception)
3879
{
3880
  const char
3881
    *const CompositeMenu[] =
3882
    {
3883
      "Operators",
3884
      "Dissolve",
3885
      "Displace",
3886
      "Help",
3887
      "Dismiss",
3888
      (char *) NULL
3889
    };
3890
3891
  static char
3892
    displacement_geometry[MagickPathExtent] = "30x30",
3893
    filename[MagickPathExtent] = "\0";
3894
3895
  static CompositeOperator
3896
    compose = CopyCompositeOp;
3897
3898
  static const ModeType
3899
    CompositeCommands[] =
3900
    {
3901
      CompositeOperatorsCommand,
3902
      CompositeDissolveCommand,
3903
      CompositeDisplaceCommand,
3904
      CompositeHelpCommand,
3905
      CompositeDismissCommand
3906
    };
3907
3908
  char
3909
    text[MagickPathExtent];
3910
3911
  Cursor
3912
    cursor;
3913
3914
  Image
3915
    *composite_image;
3916
3917
  int
3918
    entry,
3919
    id,
3920
    x,
3921
    y;
3922
3923
  double
3924
    blend,
3925
    scale_factor;
3926
3927
  RectangleInfo
3928
    highlight_info,
3929
    composite_info;
3930
3931
  unsigned int
3932
    height,
3933
    width;
3934
3935
  size_t
3936
    state;
3937
3938
  XEvent
3939
    event;
3940
3941
  /*
3942
    Request image file name from user.
3943
  */
3944
  XFileBrowserWidget(display,windows,"Composite",filename);
3945
  if (*filename == '\0')
3946
    return(MagickTrue);
3947
  /*
3948
    Read image.
3949
  */
3950
  XSetCursorState(display,windows,MagickTrue);
3951
  XCheckRefreshWindows(display,windows);
3952
  (void) CopyMagickString(resource_info->image_info->filename,filename,
3953
    MagickPathExtent);
3954
  composite_image=ReadImage(resource_info->image_info,exception);
3955
  CatchException(exception);
3956
  XSetCursorState(display,windows,MagickFalse);
3957
  if (composite_image == (Image *) NULL)
3958
    return(MagickFalse);
3959
  /*
3960
    Map Command widget.
3961
  */
3962
  (void) CloneString(&windows->command.name,"Composite");
3963
  windows->command.data=1;
3964
  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3965
  (void) XMapRaised(display,windows->command.id);
3966
  XClientMessage(display,windows->image.id,windows->im_protocols,
3967
    windows->im_update_widget,CurrentTime);
3968
  /*
3969
    Track pointer until button 1 is pressed.
3970
  */
3971
  XQueryPosition(display,windows->image.id,&x,&y);
3972
  (void) XSelectInput(display,windows->image.id,
3973
    windows->image.attributes.event_mask | PointerMotionMask);
3974
  composite_info.x=(ssize_t) windows->image.x+x;
3975
  composite_info.y=(ssize_t) windows->image.y+y;
3976
  composite_info.width=0;
3977
  composite_info.height=0;
3978
  cursor=XCreateFontCursor(display,XC_ul_angle);
3979
  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3980
  blend=0.0;
3981
  state=DefaultState;
3982
  do
3983
  {
3984
    if (windows->info.mapped != MagickFalse)
3985
      {
3986
        /*
3987
          Display pointer position.
3988
        */
3989
        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
3990
          (long) composite_info.x,(long) composite_info.y);
3991
        XInfoWidget(display,windows,text);
3992
      }
3993
    highlight_info=composite_info;
3994
    highlight_info.x=composite_info.x-windows->image.x;
3995
    highlight_info.y=composite_info.y-windows->image.y;
3996
    XHighlightRectangle(display,windows->image.id,
3997
      windows->image.highlight_context,&highlight_info);
3998
    /*
3999
      Wait for next event.
4000
    */
4001
    XScreenEvent(display,windows,&event,exception);
4002
    XHighlightRectangle(display,windows->image.id,
4003
      windows->image.highlight_context,&highlight_info);
4004
    if (event.xany.window == windows->command.id)
4005
      {
4006
        /*
4007
          Select a command from the Command widget.
4008
        */
4009
        id=XCommandWidget(display,windows,CompositeMenu,&event);
4010
        if (id < 0)
4011
          continue;
4012
        switch (CompositeCommands[id])
4013
        {
4014
          case CompositeOperatorsCommand:
4015
          {
4016
            char
4017
              command[MagickPathExtent],
4018
              **operators;
4019
4020
            /*
4021
              Select a command from the pop-up menu.
4022
            */
4023
            operators=GetCommandOptions(MagickComposeOptions);
4024
            if (operators == (char **) NULL)
4025
              break;
4026
            entry=XMenuWidget(display,windows,CompositeMenu[id],
4027
              (const char **) operators,command);
4028
            if (entry >= 0)
4029
              compose=(CompositeOperator) ParseCommandOption(
4030
                MagickComposeOptions,MagickFalse,operators[entry]);
4031
            operators=DestroyStringList(operators);
4032
            break;
4033
          }
4034
          case CompositeDissolveCommand:
4035
          {
4036
            static char
4037
              factor[MagickPathExtent] = "20.0";
4038
4039
            /*
4040
              Dissolve the two images a given percent.
4041
            */
4042
            (void) XSetFunction(display,windows->image.highlight_context,
4043
              GXcopy);
4044
            (void) XDialogWidget(display,windows,"Dissolve",
4045
              "Enter the blend factor (0.0 - 99.9%):",factor);
4046
            (void) XSetFunction(display,windows->image.highlight_context,
4047
              GXinvert);
4048
            if (*factor == '\0')
4049
              break;
4050
            blend=StringToDouble(factor,(char **) NULL);
4051
            compose=DissolveCompositeOp;
4052
            break;
4053
          }
4054
          case CompositeDisplaceCommand:
4055
          {
4056
            /*
4057
              Get horizontal and vertical scale displacement geometry.
4058
            */
4059
            (void) XSetFunction(display,windows->image.highlight_context,
4060
              GXcopy);
4061
            (void) XDialogWidget(display,windows,"Displace",
4062
              "Enter the horizontal and vertical scale:",displacement_geometry);
4063
            (void) XSetFunction(display,windows->image.highlight_context,
4064
              GXinvert);
4065
            if (*displacement_geometry == '\0')
4066
              break;
4067
            compose=DisplaceCompositeOp;
4068
            break;
4069
          }
4070
          case CompositeHelpCommand:
4071
          {
4072
            (void) XSetFunction(display,windows->image.highlight_context,
4073
              GXcopy);
4074
            XTextViewHelp(display,resource_info,windows,MagickFalse,
4075
              "Help Viewer - Image Composite",ImageCompositeHelp);
4076
            (void) XSetFunction(display,windows->image.highlight_context,
4077
              GXinvert);
4078
            break;
4079
          }
4080
          case CompositeDismissCommand:
4081
          {
4082
            /*
4083
              Prematurely exit.
4084
            */
4085
            state|=EscapeState;
4086
            state|=ExitState;
4087
            break;
4088
          }
4089
          default:
4090
            break;
4091
        }
4092
        continue;
4093
      }
4094
    switch (event.type)
4095
    {
4096
      case ButtonPress:
4097
      {
4098
        if (resource_info->debug != MagickFalse)
4099
          (void) LogMagickEvent(X11Event,GetMagickModule(),
4100
            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4101
            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4102
        if (event.xbutton.button != Button1)
4103
          break;
4104
        if (event.xbutton.window != windows->image.id)
4105
          break;
4106
        /*
4107
          Change cursor.
4108
        */
4109
        composite_info.width=composite_image->columns;
4110
        composite_info.height=composite_image->rows;
4111
        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4112
        composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4113
        composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4114
        break;
4115
      }
4116
      case ButtonRelease:
4117
      {
4118
        if (resource_info->debug != MagickFalse)
4119
          (void) LogMagickEvent(X11Event,GetMagickModule(),
4120
            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4121
            event.xbutton.button,event.xbutton.x,event.xbutton.y);
4122
        if (event.xbutton.button != Button1)
4123
          break;
4124
        if (event.xbutton.window != windows->image.id)
4125
          break;
4126
        if ((composite_info.width != 0) && (composite_info.height != 0))
4127
          {
4128
            /*
4129
              User has selected the location of the composite image.
4130
            */
4131
            composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4132
            composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4133
            state|=ExitState;
4134
          }
4135
        break;
4136
      }
4137
      case Expose:
4138
        break;
4139
      case KeyPress:
4140
      {
4141
        char
4142
          command[MagickPathExtent];
4143
4144
        KeySym
4145
          key_symbol;
4146
4147
        int
4148
          length;
4149
4150
        if (event.xkey.window != windows->image.id)
4151
          break;
4152
        /*
4153
          Respond to a user key press.
4154
        */
4155
        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4156
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4157
        *(command+length)='\0';
4158
        if (resource_info->debug != MagickFalse)
4159
          (void) LogMagickEvent(X11Event,GetMagickModule(),
4160
            "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4161
        switch ((int) key_symbol)
4162
        {
4163
          case XK_Escape:
4164
          case XK_F20:
4165
          {
4166
            /*
4167
              Prematurely exit.
4168
            */
4169
            composite_image=DestroyImage(composite_image);
4170
            state|=EscapeState;
4171
            state|=ExitState;
4172
            break;
4173
          }
4174
          case XK_F1:
4175
          case XK_Help:
4176
          {
4177
            (void) XSetFunction(display,windows->image.highlight_context,
4178
              GXcopy);
4179
            XTextViewHelp(display,resource_info,windows,MagickFalse,
4180
              "Help Viewer - Image Composite",ImageCompositeHelp);
4181
            (void) XSetFunction(display,windows->image.highlight_context,
4182
              GXinvert);
4183
            break;
4184
          }
4185
          default:
4186
          {
4187
            (void) XBell(display,0);
4188
            break;
4189
          }
4190
        }
4191
        break;
4192
      }
4193
      case MotionNotify:
4194
      {
4195
        /*
4196
          Map and unmap Info widget as text cursor crosses its boundaries.
4197
        */
4198
        x=event.xmotion.x;
4199
        y=event.xmotion.y;
4200
        if (windows->info.mapped != MagickFalse)
4201
          {
4202
            if ((x < (windows->info.x+(int) windows->info.width)) &&
4203
                (y < (windows->info.y+(int) windows->info.height)))
4204
              (void) XWithdrawWindow(display,windows->info.id,
4205
                windows->info.screen);
4206
          }
4207
        else
4208
          if ((x > (windows->info.x+(int) windows->info.width)) ||
4209
              (y > (windows->info.y+(int) windows->info.height)))
4210
            (void) XMapWindow(display,windows->info.id);
4211
        composite_info.x=(ssize_t) windows->image.x+x;
4212
        composite_info.y=(ssize_t) windows->image.y+y;
4213
        break;
4214
      }
4215
      default:
4216
      {
4217
        if (resource_info->debug != MagickFalse)
4218
          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4219
            event.type);
4220
        break;
4221
      }
4222
    }
4223
  } while ((state & ExitState) == 0);
4224
  (void) XSelectInput(display,windows->image.id,
4225
    windows->image.attributes.event_mask);
4226
  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4227
  XSetCursorState(display,windows,MagickFalse);
4228
  (void) XFreeCursor(display,cursor);
4229
  if ((state & EscapeState) != 0)
4230
    return(MagickTrue);
4231
  /*
4232
    Image compositing is relative to image configuration.
4233
  */
4234
  XSetCursorState(display,windows,MagickTrue);
4235
  XCheckRefreshWindows(display,windows);
4236
  width=(unsigned int) image->columns;
4237
  height=(unsigned int) image->rows;
4238
  x=0;
4239
  y=0;
4240
  if (windows->image.crop_geometry != (char *) NULL)
4241
    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4242
  scale_factor=(double) width/windows->image.ximage->width;
4243
  composite_info.x+=x;
4244
  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4245
  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4246
  scale_factor=(double) height/windows->image.ximage->height;
4247
  composite_info.y+=y;
4248
  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4249
  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4250
  if ((composite_info.width != composite_image->columns) ||
4251
      (composite_info.height != composite_image->rows))
4252
    {
4253
      Image
4254
        *resize_image;
4255
4256
      /*
4257
        Scale composite image.
4258
      */
4259
      resize_image=ResizeImage(composite_image,composite_info.width,
4260
        composite_info.height,composite_image->filter,exception);
4261
      composite_image=DestroyImage(composite_image);
4262
      if (resize_image == (Image *) NULL)
4263
        {
4264
          XSetCursorState(display,windows,MagickFalse);
4265
          return(MagickFalse);
4266
        }
4267
      composite_image=resize_image;
4268
    }
4269
  if (compose == DisplaceCompositeOp)
4270
    (void) SetImageArtifact(composite_image,"compose:args",
4271
      displacement_geometry);
4272
  if (blend != 0.0)
4273
    {
4274
      CacheView
4275
        *image_view;
4276
4277
      int
4278
        y;
4279
4280
      Quantum
4281
        opacity;
4282
4283
      int
4284
        x;
4285
4286
      Quantum
4287
        *q;
4288
4289
      /*
4290
        Create mattes for blending.
4291
      */
4292
      (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4293
      opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4294
        ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4295
      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4296
        return(MagickFalse);
4297
      image->alpha_trait=BlendPixelTrait;
4298
      image_view=AcquireAuthenticCacheView(image,exception);
4299
      for (y=0; y < (int) image->rows; y++)
4300
      {
4301
        q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4302
          exception);
4303
        if (q == (Quantum *) NULL)
4304
          break;
4305
        for (x=0; x < (int) image->columns; x++)
4306
        {
4307
          SetPixelAlpha(image,opacity,q);
4308
          q+=(ptrdiff_t) GetPixelChannels(image);
4309
        }
4310
        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4311
          break;
4312
      }
4313
      image_view=DestroyCacheView(image_view);
4314
    }
4315
  /*
4316
    Composite image with X Image window.
4317
  */
4318
  (void) CompositeImage(image,composite_image,compose,MagickTrue,
4319
    composite_info.x,composite_info.y,exception);
4320
  composite_image=DestroyImage(composite_image);
4321
  XSetCursorState(display,windows,MagickFalse);
4322
  /*
4323
    Update image configuration.
4324
  */
4325
  XConfigureImageColormap(display,resource_info,windows,image,exception);
4326
  (void) XConfigureImage(display,resource_info,windows,image,exception);
4327
  return(MagickTrue);
4328
}
4329

4330
/*
4331
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4332
%                                                                             %
4333
%                                                                             %
4334
%                                                                             %
4335
+   X C o n f i g u r e I m a g e                                             %
4336
%                                                                             %
4337
%                                                                             %
4338
%                                                                             %
4339
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4340
%
4341
%  XConfigureImage() creates a new X image.  It also notifies the window
4342
%  manager of the new image size and configures the transient widows.
4343
%
4344
%  The format of the XConfigureImage method is:
4345
%
4346
%      MagickBooleanType XConfigureImage(Display *display,
4347
%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4348
%        ExceptionInfo *exception)
4349
%
4350
%  A description of each parameter follows:
4351
%
4352
%    o display: Specifies a connection to an X server; returned from
4353
%      XOpenDisplay.
4354
%
4355
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4356
%
4357
%    o windows: Specifies a pointer to a XWindows structure.
4358
%
4359
%    o image: the image.
4360
%
4361
%    o exception: return any errors or warnings in this structure.
4362
%
4363
%    o exception: return any errors or warnings in this structure.
4364
%
4365
*/
4366
static MagickBooleanType XConfigureImage(Display *display,
4367
  XResourceInfo *resource_info,XWindows *windows,Image *image,
4368
  ExceptionInfo *exception)
4369
{
4370
  char
4371
    geometry[MagickPathExtent];
4372
4373
  MagickStatusType
4374
    status;
4375
4376
  size_t
4377
    mask,
4378
    height,
4379
    width;
4380
4381
  ssize_t
4382
    x,
4383
    y;
4384
4385
  XSizeHints
4386
    *size_hints;
4387
4388
  XWindowChanges
4389
    window_changes;
4390
4391
  /*
4392
    Dismiss if window dimensions are zero.
4393
  */
4394
  width=(unsigned int) windows->image.window_changes.width;
4395
  height=(unsigned int) windows->image.window_changes.height;
4396
  if (resource_info->debug != MagickFalse)
4397
    (void) LogMagickEvent(X11Event,GetMagickModule(),
4398
      "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4399
      windows->image.ximage->height,(double) width,(double) height);
4400
  if ((width*height) == 0)
4401
    return(MagickTrue);
4402
  x=0;
4403
  y=0;
4404
  /*
4405
    Resize image to fit Image window dimensions.
4406
  */
4407
  XSetCursorState(display,windows,MagickTrue);
4408
  (void) XFlush(display);
4409
  if (((int) width != windows->image.ximage->width) ||
4410
      ((int) height != windows->image.ximage->height))
4411
    image->taint=MagickTrue;
4412
  windows->magnify.x=(int)
4413
    width*windows->magnify.x/windows->image.ximage->width;
4414
  windows->magnify.y=(int)
4415
    height*windows->magnify.y/windows->image.ximage->height;
4416
  windows->image.x=((int) width*windows->image.x/windows->image.ximage->width);
4417
  windows->image.y=((int) height*windows->image.y/
4418
    windows->image.ximage->height);
4419
  status=XMakeImage(display,resource_info,&windows->image,image,
4420
    (unsigned int) width,(unsigned int) height,exception);
4421
  if (status == MagickFalse)
4422
    XNoticeWidget(display,windows,"Unable to configure X image:",
4423
      windows->image.name);
4424
  /*
4425
    Notify window manager of the new configuration.
4426
  */
4427
  if (resource_info->image_geometry != (char *) NULL)
4428
    (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4429
      resource_info->image_geometry);
4430
  else
4431
    (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4432
      XDisplayWidth(display,windows->image.screen),
4433
      XDisplayHeight(display,windows->image.screen));
4434
  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4435
  window_changes.width=(int) width;
4436
  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4437
    window_changes.width=XDisplayWidth(display,windows->image.screen);
4438
  window_changes.height=(int) height;
4439
  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4440
    window_changes.height=XDisplayHeight(display,windows->image.screen);
4441
  mask=(size_t) (CWWidth | CWHeight);
4442
  if (resource_info->backdrop)
4443
    {
4444
      mask|=CWX | CWY;
4445
      window_changes.x=((XDisplayWidth(display,windows->image.screen)/2)-
4446
        ((int) width/2));
4447
      window_changes.y=((XDisplayHeight(display,windows->image.screen)/2)-
4448
        ((int) height/2));
4449
    }
4450
  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4451
    (unsigned int) mask,&window_changes);
4452
  (void) XClearWindow(display,windows->image.id);
4453
  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4454
  /*
4455
    Update Magnify window configuration.
4456
  */
4457
  if (windows->magnify.mapped != MagickFalse)
4458
    XMakeMagnifyImage(display,windows,exception);
4459
  windows->pan.crop_geometry=windows->image.crop_geometry;
4460
  XBestIconSize(display,&windows->pan,image);
4461
  while (((windows->pan.width << 1) < MaxIconSize) &&
4462
         ((windows->pan.height << 1) < MaxIconSize))
4463
  {
4464
    windows->pan.width<<=1;
4465
    windows->pan.height<<=1;
4466
  }
4467
  if (windows->pan.geometry != (char *) NULL)
4468
    (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4469
      &windows->pan.width,&windows->pan.height);
4470
  window_changes.width=(int) windows->pan.width;
4471
  window_changes.height=(int) windows->pan.height;
4472
  size_hints=XAllocSizeHints();
4473
  if (size_hints != (XSizeHints *) NULL)
4474
    {
4475
      /*
4476
        Set new size hints.
4477
      */
4478
      size_hints->flags=PSize | PMinSize | PMaxSize;
4479
      size_hints->width=window_changes.width;
4480
      size_hints->height=window_changes.height;
4481
      size_hints->min_width=size_hints->width;
4482
      size_hints->min_height=size_hints->height;
4483
      size_hints->max_width=size_hints->width;
4484
      size_hints->max_height=size_hints->height;
4485
      (void) XSetNormalHints(display,windows->pan.id,size_hints);
4486
      (void) XFree((void *) size_hints);
4487
    }
4488
  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4489
    (unsigned int) (CWWidth | CWHeight),&window_changes);
4490
  /*
4491
    Update icon window configuration.
4492
  */
4493
  windows->icon.crop_geometry=windows->image.crop_geometry;
4494
  XBestIconSize(display,&windows->icon,image);
4495
  window_changes.width=(int) windows->icon.width;
4496
  window_changes.height=(int) windows->icon.height;
4497
  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4498
    (unsigned int) (CWWidth | CWHeight),&window_changes);
4499
  XSetCursorState(display,windows,MagickFalse);
4500
  return(status != 0 ? MagickTrue : MagickFalse);
4501
}
4502

4503
/*
4504
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4505
%                                                                             %
4506
%                                                                             %
4507
%                                                                             %
4508
+   X C r o p I m a g e                                                       %
4509
%                                                                             %
4510
%                                                                             %
4511
%                                                                             %
4512
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4513
%
4514
%  XCropImage() allows the user to select a region of the image and crop, copy,
4515
%  or cut it.  For copy or cut, the image can subsequently be composited onto
4516
%  the image with XPasteImage.
4517
%
4518
%  The format of the XCropImage method is:
4519
%
4520
%      MagickBooleanType XCropImage(Display *display,
4521
%        XResourceInfo *resource_info,XWindows *windows,Image *image,
4522
%        const ClipboardMode mode,ExceptionInfo *exception)
4523
%
4524
%  A description of each parameter follows:
4525
%
4526
%    o display: Specifies a connection to an X server; returned from
4527
%      XOpenDisplay.
4528
%
4529
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4530
%
4531
%    o windows: Specifies a pointer to a XWindows structure.
4532
%
4533
%    o image: the image; returned from ReadImage.
4534
%
4535
%    o mode: This unsigned value specified whether the image should be
4536
%      cropped, copied, or cut.
4537
%
4538
%    o exception: return any errors or warnings in this structure.
4539
%
4540
*/
4541
static MagickBooleanType XCropImage(Display *display,
4542
  XResourceInfo *resource_info,XWindows *windows,Image *image,
4543
  const ClipboardMode mode,ExceptionInfo *exception)
4544
{
4545
  const char
4546
    *const CropModeMenu[] =
4547
    {
4548
      "Help",
4549
      "Dismiss",
4550
      (char *) NULL
4551
    },
4552
    *RectifyModeMenu[] =
4553
    {
4554
      "Crop",
4555
      "Help",
4556
      "Dismiss",
4557
      (char *) NULL
4558
    };
4559
4560
  static const ModeType
4561
    CropCommands[] =
4562
    {
4563
      CropHelpCommand,
4564
      CropDismissCommand
4565
    },
4566
    RectifyCommands[] =
4567
    {
4568
      RectifyCopyCommand,
4569
      RectifyHelpCommand,
4570
      RectifyDismissCommand
4571
    };
4572
4573
  CacheView
4574
    *image_view;
4575
4576
  char
4577
    command[MagickPathExtent],
4578
    text[MagickPathExtent];
4579
4580
  Cursor
4581
    cursor;
4582
4583
  int
4584
    id,
4585
    x,
4586
    y;
4587
4588
  KeySym
4589
    key_symbol;
4590
4591
  Image
4592
    *crop_image;
4593
4594
  double
4595
    scale_factor;
4596
4597
  RectangleInfo
4598
    crop_info,
4599
    highlight_info;
4600
4601
  Quantum
4602
    *q;
4603
4604
  unsigned int
4605
    height,
4606
    width;
4607
4608
  size_t
4609
    state;
4610
4611
  XEvent
4612
    event;
4613
4614
  /*
4615
    Map Command widget.
4616
  */
4617
  switch (mode)
4618
  {
4619
    case CopyMode:
4620
    {
4621
      (void) CloneString(&windows->command.name,"Copy");
4622
      break;
4623
    }
4624
    case CropMode:
4625
    {
4626
      (void) CloneString(&windows->command.name,"Crop");
4627
      break;
4628
    }
4629
    case CutMode:
4630
    {
4631
      (void) CloneString(&windows->command.name,"Cut");
4632
      break;
4633
    }
4634
  }
4635
  RectifyModeMenu[0]=windows->command.name;
4636
  windows->command.data=0;
4637
  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4638
  (void) XMapRaised(display,windows->command.id);
4639
  XClientMessage(display,windows->image.id,windows->im_protocols,
4640
    windows->im_update_widget,CurrentTime);
4641
  /*
4642
    Track pointer until button 1 is pressed.
4643
  */
4644
  XQueryPosition(display,windows->image.id,&x,&y);
4645
  (void) XSelectInput(display,windows->image.id,
4646
    windows->image.attributes.event_mask | PointerMotionMask);
4647
  crop_info.x=(ssize_t) windows->image.x+x;
4648
  crop_info.y=(ssize_t) windows->image.y+y;
4649
  crop_info.width=0;
4650
  crop_info.height=0;
4651
  cursor=XCreateFontCursor(display,XC_fleur);
4652
  state=DefaultState;
4653
  do
4654
  {
4655
    if (windows->info.mapped != MagickFalse)
4656
      {
4657
        /*
4658
          Display pointer position.
4659
        */
4660
        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4661
          (long) crop_info.x,(long) crop_info.y);
4662
        XInfoWidget(display,windows,text);
4663
      }
4664
    /*
4665
      Wait for next event.
4666
    */
4667
    XScreenEvent(display,windows,&event,exception);
4668
    if (event.xany.window == windows->command.id)
4669
      {
4670
        /*
4671
          Select a command from the Command widget.
4672
        */
4673
        id=XCommandWidget(display,windows,CropModeMenu,&event);
4674
        if (id < 0)
4675
          continue;
4676
        switch (CropCommands[id])
4677
        {
4678
          case CropHelpCommand:
4679
          {
4680
            switch (mode)
4681
            {
4682
              case CopyMode:
4683
              {
4684
                XTextViewHelp(display,resource_info,windows,MagickFalse,
4685
                  "Help Viewer - Image Copy",ImageCopyHelp);
4686
                break;
4687
              }
4688
              case CropMode:
4689
              {
4690
                XTextViewHelp(display,resource_info,windows,MagickFalse,
4691
                  "Help Viewer - Image Crop",ImageCropHelp);
4692
                break;
4693
              }
4694
              case CutMode:
4695
              {
4696
                XTextViewHelp(display,resource_info,windows,MagickFalse,
4697
                  "Help Viewer - Image Cut",ImageCutHelp);
4698
                break;
4699
              }
4700
            }
4701
            break;
4702
          }
4703
          case CropDismissCommand:
4704
          {
4705
            /*
4706
              Prematurely exit.
4707
            */
4708
            state|=EscapeState;
4709
            state|=ExitState;
4710
            break;
4711
          }
4712
          default:
4713
            break;
4714
        }
4715
        continue;
4716
      }
4717
    switch (event.type)
4718
    {
4719
      case ButtonPress:
4720
      {
4721
        if (event.xbutton.button != Button1)
4722
          break;
4723
        if (event.xbutton.window != windows->image.id)
4724
          break;
4725
        /*
4726
          Note first corner of cropping rectangle-- exit loop.
4727
        */
4728
        (void) XCheckDefineCursor(display,windows->image.id,cursor);
4729
        crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4730
        crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4731
        state|=ExitState;
4732
        break;
4733
      }
4734
      case ButtonRelease:
4735
        break;
4736
      case Expose:
4737
        break;
4738
      case KeyPress:
4739
      {
4740
        if (event.xkey.window != windows->image.id)
4741
          break;
4742
        /*
4743
          Respond to a user key press.
4744
        */
4745
        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4746
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4747
        switch ((int) key_symbol)
4748
        {
4749
          case XK_Escape:
4750
          case XK_F20:
4751
          {
4752
            /*
4753
              Prematurely exit.
4754
            */
4755
            state|=EscapeState;
4756
            state|=ExitState;
4757
            break;
4758
          }
4759
          case XK_F1:
4760
          case XK_Help:
4761
          {
4762
            switch (mode)
4763
            {
4764
              case CopyMode:
4765
              {
4766
                XTextViewHelp(display,resource_info,windows,MagickFalse,
4767
                  "Help Viewer - Image Copy",ImageCopyHelp);
4768
                break;
4769
              }
4770
              case CropMode:
4771
              {
4772
                XTextViewHelp(display,resource_info,windows,MagickFalse,
4773
                  "Help Viewer - Image Crop",ImageCropHelp);
4774
                break;
4775
              }
4776
              case CutMode:
4777
              {
4778
                XTextViewHelp(display,resource_info,windows,MagickFalse,
4779
                  "Help Viewer - Image Cut",ImageCutHelp);
4780
                break;
4781
              }
4782
            }
4783
            break;
4784
          }
4785
          default:
4786
          {
4787
            (void) XBell(display,0);
4788
            break;
4789
          }
4790
        }
4791
        break;
4792
      }
4793
      case MotionNotify:
4794
      {
4795
        if (event.xmotion.window != windows->image.id)
4796
          break;
4797
        /*
4798
          Map and unmap Info widget as text cursor crosses its boundaries.
4799
        */
4800
        x=event.xmotion.x;
4801
        y=event.xmotion.y;
4802
        if (windows->info.mapped != MagickFalse)
4803
          {
4804
            if ((x < (windows->info.x+(int) windows->info.width)) &&
4805
                (y < (windows->info.y+(int) windows->info.height)))
4806
              (void) XWithdrawWindow(display,windows->info.id,
4807
                windows->info.screen);
4808
          }
4809
        else
4810
          if ((x > (windows->info.x+(int) windows->info.width)) ||
4811
              (y > (windows->info.y+(int) windows->info.height)))
4812
            (void) XMapWindow(display,windows->info.id);
4813
        crop_info.x=(ssize_t) windows->image.x+x;
4814
        crop_info.y=(ssize_t) windows->image.y+y;
4815
        break;
4816
      }
4817
      default:
4818
        break;
4819
    }
4820
  } while ((state & ExitState) == 0);
4821
  (void) XSelectInput(display,windows->image.id,
4822
    windows->image.attributes.event_mask);
4823
  if ((state & EscapeState) != 0)
4824
    {
4825
      /*
4826
        User want to exit without cropping.
4827
      */
4828
      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4829
      (void) XFreeCursor(display,cursor);
4830
      return(MagickTrue);
4831
    }
4832
  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4833
  do
4834
  {
4835
    /*
4836
      Size rectangle as pointer moves until the mouse button is released.
4837
    */
4838
    x=(int) crop_info.x;
4839
    y=(int) crop_info.y;
4840
    crop_info.width=0;
4841
    crop_info.height=0;
4842
    state=DefaultState;
4843
    do
4844
    {
4845
      highlight_info=crop_info;
4846
      highlight_info.x=crop_info.x-windows->image.x;
4847
      highlight_info.y=crop_info.y-windows->image.y;
4848
      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4849
        {
4850
          /*
4851
            Display info and draw cropping rectangle.
4852
          */
4853
          if (windows->info.mapped == MagickFalse)
4854
            (void) XMapWindow(display,windows->info.id);
4855
          (void) FormatLocaleString(text,MagickPathExtent,
4856
            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4857
            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4858
          XInfoWidget(display,windows,text);
4859
          XHighlightRectangle(display,windows->image.id,
4860
            windows->image.highlight_context,&highlight_info);
4861
        }
4862
      else
4863
        if (windows->info.mapped != MagickFalse)
4864
          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4865
      /*
4866
        Wait for next event.
4867
      */
4868
      XScreenEvent(display,windows,&event,exception);
4869
      if ((highlight_info.width > 3) && (highlight_info.height > 3))
4870
        XHighlightRectangle(display,windows->image.id,
4871
          windows->image.highlight_context,&highlight_info);
4872
      switch (event.type)
4873
      {
4874
        case ButtonPress:
4875
        {
4876
          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4877
          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4878
          break;
4879
        }
4880
        case ButtonRelease:
4881
        {
4882
          /*
4883
            User has committed to cropping rectangle.
4884
          */
4885
          crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4886
          crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4887
          XSetCursorState(display,windows,MagickFalse);
4888
          state|=ExitState;
4889
          windows->command.data=0;
4890
          (void) XCommandWidget(display,windows,RectifyModeMenu,
4891
            (XEvent *) NULL);
4892
          break;
4893
        }
4894
        case Expose:
4895
          break;
4896
        case MotionNotify:
4897
        {
4898
          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4899
          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4900
        }
4901
        default:
4902
          break;
4903
      }
4904
      if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4905
          ((state & ExitState) != 0))
4906
        {
4907
          /*
4908
            Check boundary conditions.
4909
          */
4910
          if (crop_info.x < 0)
4911
            crop_info.x=0;
4912
          else
4913
            if (crop_info.x > (ssize_t) windows->image.ximage->width)
4914
              crop_info.x=(ssize_t) windows->image.ximage->width;
4915
          if ((int) crop_info.x < x)
4916
            crop_info.width=(unsigned int) (x-crop_info.x);
4917
          else
4918
            {
4919
              crop_info.width=(unsigned int) (crop_info.x-x);
4920
              crop_info.x=(ssize_t) x;
4921
            }
4922
          if (crop_info.y < 0)
4923
            crop_info.y=0;
4924
          else
4925
            if (crop_info.y > (ssize_t) windows->image.ximage->height)
4926
              crop_info.y=(ssize_t) windows->image.ximage->height;
4927
          if ((int) crop_info.y < y)
4928
            crop_info.height=(unsigned int) (y-crop_info.y);
4929
          else
4930
            {
4931
              crop_info.height=(unsigned int) (crop_info.y-y);
4932
              crop_info.y=(ssize_t) y;
4933
            }
4934
        }
4935
    } while ((state & ExitState) == 0);
4936
    /*
4937
      Wait for user to grab a corner of the rectangle or press return.
4938
    */
4939
    state=DefaultState;
4940
    (void) XMapWindow(display,windows->info.id);
4941
    do
4942
    {
4943
      if (windows->info.mapped != MagickFalse)
4944
        {
4945
          /*
4946
            Display pointer position.
4947
          */
4948
          (void) FormatLocaleString(text,MagickPathExtent,
4949
            " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4950
            crop_info.height,(double) crop_info.x,(double) crop_info.y);
4951
          XInfoWidget(display,windows,text);
4952
        }
4953
      highlight_info=crop_info;
4954
      highlight_info.x=crop_info.x-windows->image.x;
4955
      highlight_info.y=crop_info.y-windows->image.y;
4956
      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4957
        {
4958
          state|=EscapeState;
4959
          state|=ExitState;
4960
          break;
4961
        }
4962
      XHighlightRectangle(display,windows->image.id,
4963
        windows->image.highlight_context,&highlight_info);
4964
      XScreenEvent(display,windows,&event,exception);
4965
      if (event.xany.window == windows->command.id)
4966
        {
4967
          /*
4968
            Select a command from the Command widget.
4969
          */
4970
          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4971
          id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4972
          (void) XSetFunction(display,windows->image.highlight_context,
4973
            GXinvert);
4974
          XHighlightRectangle(display,windows->image.id,
4975
            windows->image.highlight_context,&highlight_info);
4976
          if (id >= 0)
4977
            switch (RectifyCommands[id])
4978
            {
4979
              case RectifyCopyCommand:
4980
              {
4981
                state|=ExitState;
4982
                break;
4983
              }
4984
              case RectifyHelpCommand:
4985
              {
4986
                (void) XSetFunction(display,windows->image.highlight_context,
4987
                  GXcopy);
4988
                switch (mode)
4989
                {
4990
                  case CopyMode:
4991
                  {
4992
                    XTextViewHelp(display,resource_info,windows,MagickFalse,
4993
                      "Help Viewer - Image Copy",ImageCopyHelp);
4994
                    break;
4995
                  }
4996
                  case CropMode:
4997
                  {
4998
                    XTextViewHelp(display,resource_info,windows,MagickFalse,
4999
                      "Help Viewer - Image Crop",ImageCropHelp);
5000
                    break;
5001
                  }
5002
                  case CutMode:
5003
                  {
5004
                    XTextViewHelp(display,resource_info,windows,MagickFalse,
5005
                      "Help Viewer - Image Cut",ImageCutHelp);
5006
                    break;
5007
                  }
5008
                }
5009
                (void) XSetFunction(display,windows->image.highlight_context,
5010
                  GXinvert);
5011
                break;
5012
              }
5013
              case RectifyDismissCommand:
5014
              {
5015
                /*
5016
                  Prematurely exit.
5017
                */
5018
                state|=EscapeState;
5019
                state|=ExitState;
5020
                break;
5021
              }
5022
              default:
5023
                break;
5024
            }
5025
          continue;
5026
        }
5027
      XHighlightRectangle(display,windows->image.id,
5028
        windows->image.highlight_context,&highlight_info);
5029
      switch (event.type)
5030
      {
5031
        case ButtonPress:
5032
        {
5033
          if (event.xbutton.button != Button1)
5034
            break;
5035
          if (event.xbutton.window != windows->image.id)
5036
            break;
5037
          x=windows->image.x+event.xbutton.x;
5038
          y=windows->image.y+event.xbutton.y;
5039
          if ((x < (int) (crop_info.x+RoiDelta)) &&
5040
              (x > (int) (crop_info.x-RoiDelta)) &&
5041
              (y < (int) (crop_info.y+RoiDelta)) &&
5042
              (y > (int) (crop_info.y-RoiDelta)))
5043
            {
5044
              crop_info.x=crop_info.x+(ssize_t) crop_info.width;
5045
              crop_info.y=crop_info.y+(ssize_t) crop_info.height;
5046
              state|=UpdateConfigurationState;
5047
              break;
5048
            }
5049
          if ((x < (int) (crop_info.x+RoiDelta)) &&
5050
              (x > (int) (crop_info.x-RoiDelta)) &&
5051
              (y < (int) (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5052
              (y > (int) (crop_info.y+(int) crop_info.height-RoiDelta)))
5053
            {
5054
              crop_info.x=(crop_info.x+(int) crop_info.width);
5055
              state|=UpdateConfigurationState;
5056
              break;
5057
            }
5058
          if ((x < (int) (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5059
              (x > (int) (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5060
              (y < (int) (crop_info.y+RoiDelta)) &&
5061
              (y > (int) (crop_info.y-RoiDelta)))
5062
            {
5063
              crop_info.y=(crop_info.y+(ssize_t) crop_info.height);
5064
              state|=UpdateConfigurationState;
5065
              break;
5066
            }
5067
          if ((x < (int) (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5068
              (x > (int) (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5069
              (y < (int) (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5070
              (y > (int) (crop_info.y+(int) crop_info.height-RoiDelta)))
5071
            {
5072
              state|=UpdateConfigurationState;
5073
              break;
5074
            }
5075
          magick_fallthrough;
5076
        }
5077
        case ButtonRelease:
5078
        {
5079
          if (event.xbutton.window == windows->pan.id)
5080
            if ((highlight_info.x != crop_info.x-windows->image.x) ||
5081
                (highlight_info.y != crop_info.y-windows->image.y))
5082
              XHighlightRectangle(display,windows->image.id,
5083
                windows->image.highlight_context,&highlight_info);
5084
          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5085
            event.xbutton.time);
5086
          break;
5087
        }
5088
        case Expose:
5089
        {
5090
          if (event.xexpose.window == windows->image.id)
5091
            if (event.xexpose.count == 0)
5092
              {
5093
                event.xexpose.x=(int) highlight_info.x;
5094
                event.xexpose.y=(int) highlight_info.y;
5095
                event.xexpose.width=(int) highlight_info.width;
5096
                event.xexpose.height=(int) highlight_info.height;
5097
                XRefreshWindow(display,&windows->image,&event);
5098
              }
5099
          if (event.xexpose.window == windows->info.id)
5100
            if (event.xexpose.count == 0)
5101
              XInfoWidget(display,windows,text);
5102
          break;
5103
        }
5104
        case KeyPress:
5105
        {
5106
          if (event.xkey.window != windows->image.id)
5107
            break;
5108
          /*
5109
            Respond to a user key press.
5110
          */
5111
          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5112
            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5113
          switch ((int) key_symbol)
5114
          {
5115
            case XK_Escape:
5116
            case XK_F20:
5117
            {
5118
              state|=EscapeState;
5119
              magick_fallthrough;
5120
            }
5121
            case XK_Return:
5122
            {
5123
              state|=ExitState;
5124
              break;
5125
            }
5126
            case XK_Home:
5127
            case XK_KP_Home:
5128
            {
5129
              crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5130
                2L);
5131
              crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5132
                2L);
5133
              break;
5134
            }
5135
            case XK_Left:
5136
            case XK_KP_Left:
5137
            {
5138
              crop_info.x--;
5139
              break;
5140
            }
5141
            case XK_Up:
5142
            case XK_KP_Up:
5143
            case XK_Next:
5144
            {
5145
              crop_info.y--;
5146
              break;
5147
            }
5148
            case XK_Right:
5149
            case XK_KP_Right:
5150
            {
5151
              crop_info.x++;
5152
              break;
5153
            }
5154
            case XK_Prior:
5155
            case XK_Down:
5156
            case XK_KP_Down:
5157
            {
5158
              crop_info.y++;
5159
              break;
5160
            }
5161
            case XK_F1:
5162
            case XK_Help:
5163
            {
5164
              (void) XSetFunction(display,windows->image.highlight_context,
5165
                GXcopy);
5166
              switch (mode)
5167
              {
5168
                case CopyMode:
5169
                {
5170
                  XTextViewHelp(display,resource_info,windows,MagickFalse,
5171
                    "Help Viewer - Image Copy",ImageCopyHelp);
5172
                  break;
5173
                }
5174
                case CropMode:
5175
                {
5176
                  XTextViewHelp(display,resource_info,windows,MagickFalse,
5177
                    "Help Viewer - Image Cropg",ImageCropHelp);
5178
                  break;
5179
                }
5180
                case CutMode:
5181
                {
5182
                  XTextViewHelp(display,resource_info,windows,MagickFalse,
5183
                    "Help Viewer - Image Cutg",ImageCutHelp);
5184
                  break;
5185
                }
5186
              }
5187
              (void) XSetFunction(display,windows->image.highlight_context,
5188
                GXinvert);
5189
              break;
5190
            }
5191
            default:
5192
            {
5193
              (void) XBell(display,0);
5194
              break;
5195
            }
5196
          }
5197
          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5198
            event.xkey.time);
5199
          break;
5200
        }
5201
        case KeyRelease:
5202
          break;
5203
        case MotionNotify:
5204
        {
5205
          if (event.xmotion.window != windows->image.id)
5206
            break;
5207
          /*
5208
            Map and unmap Info widget as text cursor crosses its boundaries.
5209
          */
5210
          x=event.xmotion.x;
5211
          y=event.xmotion.y;
5212
          if (windows->info.mapped != MagickFalse)
5213
            {
5214
              if ((x < (windows->info.x+(int) windows->info.width)) &&
5215
                  (y < (windows->info.y+(int) windows->info.height)))
5216
                (void) XWithdrawWindow(display,windows->info.id,
5217
                  windows->info.screen);
5218
            }
5219
          else
5220
            if ((x > (windows->info.x+(int) windows->info.width)) ||
5221
                (y > (windows->info.y+(int) windows->info.height)))
5222
              (void) XMapWindow(display,windows->info.id);
5223
          crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5224
          crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5225
          break;
5226
        }
5227
        case SelectionRequest:
5228
        {
5229
          XSelectionEvent
5230
            notify;
5231
5232
          XSelectionRequestEvent
5233
            *request;
5234
5235
          /*
5236
            Set primary selection.
5237
          */
5238
          (void) FormatLocaleString(text,MagickPathExtent,
5239
            "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5240
            crop_info.height,(double) crop_info.x,(double) crop_info.y);
5241
          request=(&(event.xselectionrequest));
5242
          (void) XChangeProperty(request->display,request->requestor,
5243
            request->property,request->target,8,PropModeReplace,
5244
            (unsigned char *) text,(int) strlen(text));
5245
          notify.type=SelectionNotify;
5246
          notify.display=request->display;
5247
          notify.requestor=request->requestor;
5248
          notify.selection=request->selection;
5249
          notify.target=request->target;
5250
          notify.time=request->time;
5251
          if (request->property == None)
5252
            notify.property=request->target;
5253
          else
5254
            notify.property=request->property;
5255
          (void) XSendEvent(request->display,request->requestor,False,0,
5256
            (XEvent *) &notify);
5257
        }
5258
        default:
5259
          break;
5260
      }
5261
      if ((state & UpdateConfigurationState) != 0)
5262
        {
5263
          (void) XPutBackEvent(display,&event);
5264
          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5265
          break;
5266
        }
5267
    } while ((state & ExitState) == 0);
5268
  } while ((state & ExitState) == 0);
5269
  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5270
  XSetCursorState(display,windows,MagickFalse);
5271
  if ((state & EscapeState) != 0)
5272
    return(MagickTrue);
5273
  if (mode == CropMode)
5274
    if (((int) crop_info.width != windows->image.ximage->width) ||
5275
        ((int) crop_info.height != windows->image.ximage->height))
5276
      {
5277
        /*
5278
          Reconfigure Image window as defined by cropping rectangle.
5279
        */
5280
        XSetCropGeometry(display,windows,&crop_info,image);
5281
        windows->image.window_changes.width=(int) crop_info.width;
5282
        windows->image.window_changes.height=(int) crop_info.height;
5283
        (void) XConfigureImage(display,resource_info,windows,image,exception);
5284
        return(MagickTrue);
5285
      }
5286
  /*
5287
    Copy image before applying image transforms.
5288
  */
5289
  XSetCursorState(display,windows,MagickTrue);
5290
  XCheckRefreshWindows(display,windows);
5291
  width=(unsigned int) image->columns;
5292
  height=(unsigned int) image->rows;
5293
  x=0;
5294
  y=0;
5295
  if (windows->image.crop_geometry != (char *) NULL)
5296
    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5297
  scale_factor=(double) width/windows->image.ximage->width;
5298
  crop_info.x+=x;
5299
  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5300
  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5301
  scale_factor=(double) height/windows->image.ximage->height;
5302
  crop_info.y+=y;
5303
  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5304
  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5305
  crop_info.x+=image->page.x;
5306
  crop_info.y+=image->page.y;
5307
  crop_image=CropImage(image,&crop_info,exception);
5308
  XSetCursorState(display,windows,MagickFalse);
5309
  if (crop_image == (Image *) NULL)
5310
    return(MagickFalse);
5311
  if (resource_info->copy_image != (Image *) NULL)
5312
    resource_info->copy_image=DestroyImage(resource_info->copy_image);
5313
  resource_info->copy_image=crop_image;
5314
  if (mode == CopyMode)
5315
    {
5316
      (void) XConfigureImage(display,resource_info,windows,image,exception);
5317
      return(MagickTrue);
5318
    }
5319
  /*
5320
    Cut image.
5321
  */
5322
  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5323
    return(MagickFalse);
5324
  image->alpha_trait=BlendPixelTrait;
5325
  image_view=AcquireAuthenticCacheView(image,exception);
5326
  for (y=0; y < (int) crop_info.height; y++)
5327
  {
5328
    q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5329
      crop_info.width,1,exception);
5330
    if (q == (Quantum *) NULL)
5331
      break;
5332
    for (x=0; x < (int) crop_info.width; x++)
5333
    {
5334
      SetPixelAlpha(image,TransparentAlpha,q);
5335
      q+=(ptrdiff_t) GetPixelChannels(image);
5336
    }
5337
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5338
      break;
5339
  }
5340
  image_view=DestroyCacheView(image_view);
5341
  /*
5342
    Update image configuration.
5343
  */
5344
  XConfigureImageColormap(display,resource_info,windows,image,exception);
5345
  (void) XConfigureImage(display,resource_info,windows,image,exception);
5346
  return(MagickTrue);
5347
}
5348

5349
/*
5350
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5351
%                                                                             %
5352
%                                                                             %
5353
%                                                                             %
5354
+   X D r a w I m a g e                                                       %
5355
%                                                                             %
5356
%                                                                             %
5357
%                                                                             %
5358
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5359
%
5360
%  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5361
%  the image.
5362
%
5363
%  The format of the XDrawEditImage method is:
5364
%
5365
%      MagickBooleanType XDrawEditImage(Display *display,
5366
%        XResourceInfo *resource_info,XWindows *windows,Image **image,
5367
%        ExceptionInfo *exception)
5368
%
5369
%  A description of each parameter follows:
5370
%
5371
%    o display: Specifies a connection to an X server; returned from
5372
%      XOpenDisplay.
5373
%
5374
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5375
%
5376
%    o windows: Specifies a pointer to a XWindows structure.
5377
%
5378
%    o image: the image.
5379
%
5380
%    o exception: return any errors or warnings in this structure.
5381
%
5382
*/
5383
static MagickBooleanType XDrawEditImage(Display *display,
5384
  XResourceInfo *resource_info,XWindows *windows,Image **image,
5385
  ExceptionInfo *exception)
5386
{
5387
  const char
5388
    *const DrawMenu[] =
5389
    {
5390
      "Element",
5391
      "Color",
5392
      "Stipple",
5393
      "Width",
5394
      "Undo",
5395
      "Help",
5396
      "Dismiss",
5397
      (char *) NULL
5398
    };
5399
5400
  static ElementType
5401
    element = PointElement;
5402
5403
  static const ModeType
5404
    DrawCommands[] =
5405
    {
5406
      DrawElementCommand,
5407
      DrawColorCommand,
5408
      DrawStippleCommand,
5409
      DrawWidthCommand,
5410
      DrawUndoCommand,
5411
      DrawHelpCommand,
5412
      DrawDismissCommand
5413
    };
5414
5415
  static Pixmap
5416
    stipple = (Pixmap) NULL;
5417
5418
  static unsigned int
5419
    pen_id = 0,
5420
    line_width = 1;
5421
5422
  char
5423
    command[MagickPathExtent],
5424
    text[MagickPathExtent];
5425
5426
  Cursor
5427
    cursor;
5428
5429
  int
5430
    entry,
5431
    id,
5432
    number_coordinates,
5433
    x,
5434
    y;
5435
5436
  double
5437
    degrees;
5438
5439
  MagickStatusType
5440
    status;
5441
5442
  RectangleInfo
5443
    rectangle_info;
5444
5445
  int
5446
    i;
5447
5448
  unsigned int
5449
    distance,
5450
    height,
5451
    max_coordinates,
5452
    width;
5453
5454
  size_t
5455
    state;
5456
5457
  Window
5458
    root_window;
5459
5460
  XDrawInfo
5461
    draw_info;
5462
5463
  XEvent
5464
    event;
5465
5466
  XPoint
5467
    *coordinate_info;
5468
5469
  XSegment
5470
    line_info;
5471
5472
  /*
5473
    Allocate polygon info.
5474
  */
5475
  max_coordinates=2048;
5476
  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5477
    sizeof(*coordinate_info));
5478
  if (coordinate_info == (XPoint *) NULL)
5479
    {
5480
      (void) ThrowMagickException(exception,GetMagickModule(),
5481
        ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5482
      return(MagickFalse);
5483
    }
5484
  /*
5485
    Map Command widget.
5486
  */
5487
  (void) CloneString(&windows->command.name,"Draw");
5488
  windows->command.data=4;
5489
  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5490
  (void) XMapRaised(display,windows->command.id);
5491
  XClientMessage(display,windows->image.id,windows->im_protocols,
5492
    windows->im_update_widget,CurrentTime);
5493
  /*
5494
    Wait for first button press.
5495
  */
5496
  root_window=XRootWindow(display,XDefaultScreen(display));
5497
  draw_info.stencil=OpaqueStencil;
5498
  status=MagickTrue;
5499
  cursor=XCreateFontCursor(display,XC_tcross);
5500
  for ( ; ; )
5501
  {
5502
    XQueryPosition(display,windows->image.id,&x,&y);
5503
    (void) XSelectInput(display,windows->image.id,
5504
      windows->image.attributes.event_mask | PointerMotionMask);
5505
    (void) XCheckDefineCursor(display,windows->image.id,cursor);
5506
    state=DefaultState;
5507
    do
5508
    {
5509
      if (windows->info.mapped != MagickFalse)
5510
        {
5511
          /*
5512
            Display pointer position.
5513
          */
5514
          (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5515
            x+windows->image.x,y+windows->image.y);
5516
          XInfoWidget(display,windows,text);
5517
        }
5518
      /*
5519
        Wait for next event.
5520
      */
5521
      XScreenEvent(display,windows,&event,exception);
5522
      if (event.xany.window == windows->command.id)
5523
        {
5524
          /*
5525
            Select a command from the Command widget.
5526
          */
5527
          id=XCommandWidget(display,windows,DrawMenu,&event);
5528
          if (id < 0)
5529
            continue;
5530
          switch (DrawCommands[id])
5531
          {
5532
            case DrawElementCommand:
5533
            {
5534
              const char
5535
                *const Elements[] =
5536
                {
5537
                  "point",
5538
                  "line",
5539
                  "rectangle",
5540
                  "fill rectangle",
5541
                  "circle",
5542
                  "fill circle",
5543
                  "ellipse",
5544
                  "fill ellipse",
5545
                  "polygon",
5546
                  "fill polygon",
5547
                  (char *) NULL,
5548
                };
5549
5550
              /*
5551
                Select a command from the pop-up menu.
5552
              */
5553
              element=(ElementType) (XMenuWidget(display,windows,
5554
                DrawMenu[id],Elements,command)+1);
5555
              break;
5556
            }
5557
            case DrawColorCommand:
5558
            {
5559
              const char
5560
                *ColorMenu[MaxNumberPens+1];
5561
5562
              int
5563
                pen_number;
5564
5565
              MagickBooleanType
5566
                transparent;
5567
5568
              XColor
5569
                color;
5570
5571
              /*
5572
                Initialize menu selections.
5573
              */
5574
              for (i=0; i < (int) (MaxNumberPens-2); i++)
5575
                ColorMenu[i]=resource_info->pen_colors[i];
5576
              ColorMenu[MaxNumberPens-2]="transparent";
5577
              ColorMenu[MaxNumberPens-1]="Browser...";
5578
              ColorMenu[MaxNumberPens]=(char *) NULL;
5579
              /*
5580
                Select a pen color from the pop-up menu.
5581
              */
5582
              pen_number=XMenuWidget(display,windows,DrawMenu[id],
5583
                (const char **) ColorMenu,command);
5584
              if (pen_number < 0)
5585
                break;
5586
              transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5587
                MagickFalse;
5588
              if (transparent != MagickFalse)
5589
                {
5590
                  draw_info.stencil=TransparentStencil;
5591
                  break;
5592
                }
5593
              if (pen_number == (MaxNumberPens-1))
5594
                {
5595
                  static char
5596
                    color_name[MagickPathExtent] = "gray";
5597
5598
                  /*
5599
                    Select a pen color from a dialog.
5600
                  */
5601
                  resource_info->pen_colors[pen_number]=color_name;
5602
                  XColorBrowserWidget(display,windows,"Select",color_name);
5603
                  if (*color_name == '\0')
5604
                    break;
5605
                }
5606
              /*
5607
                Set pen color.
5608
              */
5609
              (void) XParseColor(display,windows->map_info->colormap,
5610
                resource_info->pen_colors[pen_number],&color);
5611
              XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5612
                (unsigned int) MaxColors,&color);
5613
              windows->pixel_info->pen_colors[pen_number]=color;
5614
              pen_id=(unsigned int) pen_number;
5615
              draw_info.stencil=OpaqueStencil;
5616
              break;
5617
            }
5618
            case DrawStippleCommand:
5619
            {
5620
              const char
5621
                *StipplesMenu[] =
5622
                {
5623
                  "Brick",
5624
                  "Diagonal",
5625
                  "Scales",
5626
                  "Vertical",
5627
                  "Wavy",
5628
                  "Translucent",
5629
                  "Opaque",
5630
                  (char *) NULL,
5631
                  (char *) NULL,
5632
                };
5633
5634
              Image
5635
                *stipple_image;
5636
5637
              ImageInfo
5638
                *image_info;
5639
5640
              int
5641
                status;
5642
5643
              static char
5644
                filename[MagickPathExtent] = "\0";
5645
5646
              /*
5647
                Select a command from the pop-up menu.
5648
              */
5649
              StipplesMenu[7]="Open...";
5650
              entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5651
                command);
5652
              if (entry < 0)
5653
                break;
5654
              if (stipple != (Pixmap) NULL)
5655
                (void) XFreePixmap(display,stipple);
5656
              stipple=(Pixmap) NULL;
5657
              if (entry != 7)
5658
                {
5659
                  switch (entry)
5660
                  {
5661
                    case 0:
5662
                    {
5663
                      stipple=XCreateBitmapFromData(display,root_window,
5664
                        (char *) BricksBitmap,BricksWidth,BricksHeight);
5665
                      break;
5666
                    }
5667
                    case 1:
5668
                    {
5669
                      stipple=XCreateBitmapFromData(display,root_window,
5670
                        (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5671
                      break;
5672
                    }
5673
                    case 2:
5674
                    {
5675
                      stipple=XCreateBitmapFromData(display,root_window,
5676
                        (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5677
                      break;
5678
                    }
5679
                    case 3:
5680
                    {
5681
                      stipple=XCreateBitmapFromData(display,root_window,
5682
                        (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5683
                      break;
5684
                    }
5685
                    case 4:
5686
                    {
5687
                      stipple=XCreateBitmapFromData(display,root_window,
5688
                        (char *) WavyBitmap,WavyWidth,WavyHeight);
5689
                      break;
5690
                    }
5691
                    case 5:
5692
                    {
5693
                      stipple=XCreateBitmapFromData(display,root_window,
5694
                        (char *) HighlightBitmap,HighlightWidth,
5695
                        HighlightHeight);
5696
                      break;
5697
                    }
5698
                    case 6:
5699
                    default:
5700
                    {
5701
                      stipple=XCreateBitmapFromData(display,root_window,
5702
                        (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5703
                      break;
5704
                    }
5705
                  }
5706
                  break;
5707
                }
5708
              XFileBrowserWidget(display,windows,"Stipple",filename);
5709
              if (*filename == '\0')
5710
                break;
5711
              /*
5712
                Read image.
5713
              */
5714
              XSetCursorState(display,windows,MagickTrue);
5715
              XCheckRefreshWindows(display,windows);
5716
              image_info=AcquireImageInfo();
5717
              (void) CopyMagickString(image_info->filename,filename,
5718
                MagickPathExtent);
5719
              stipple_image=ReadImage(image_info,exception);
5720
              CatchException(exception);
5721
              XSetCursorState(display,windows,MagickFalse);
5722
              if (stipple_image == (Image *) NULL)
5723
                break;
5724
              (void) AcquireUniqueFileResource(filename);
5725
              (void) FormatLocaleString(stipple_image->filename,
5726
                MagickPathExtent,"xbm:%s",filename);
5727
              (void) WriteImage(image_info,stipple_image,exception);
5728
              stipple_image=DestroyImage(stipple_image);
5729
              image_info=DestroyImageInfo(image_info);
5730
              status=XReadBitmapFile(display,root_window,filename,&width,
5731
                &height,&stipple,&x,&y);
5732
              (void) RelinquishUniqueFileResource(filename);
5733
              if ((status != BitmapSuccess) != 0)
5734
                XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5735
                  filename);
5736
              break;
5737
            }
5738
            case DrawWidthCommand:
5739
            {
5740
              const char
5741
                *const WidthsMenu[] =
5742
                {
5743
                  "1",
5744
                  "2",
5745
                  "4",
5746
                  "8",
5747
                  "16",
5748
                  "Dialog...",
5749
                  (char *) NULL,
5750
                };
5751
5752
              static char
5753
                width[MagickPathExtent] = "0";
5754
5755
              /*
5756
                Select a command from the pop-up menu.
5757
              */
5758
              entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5759
                command);
5760
              if (entry < 0)
5761
                break;
5762
              if (entry != 5)
5763
                {
5764
                  line_width=(unsigned int) StringToUnsignedLong(
5765
                    WidthsMenu[entry]);
5766
                  break;
5767
                }
5768
              (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5769
                width);
5770
              if (*width == '\0')
5771
                break;
5772
              line_width=(unsigned int) StringToUnsignedLong(width);
5773
              break;
5774
            }
5775
            case DrawUndoCommand:
5776
            {
5777
              (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5778
                image,exception);
5779
              break;
5780
            }
5781
            case DrawHelpCommand:
5782
            {
5783
              XTextViewHelp(display,resource_info,windows,MagickFalse,
5784
                "Help Viewer - Image Rotation",ImageDrawHelp);
5785
              (void) XCheckDefineCursor(display,windows->image.id,cursor);
5786
              break;
5787
            }
5788
            case DrawDismissCommand:
5789
            {
5790
              /*
5791
                Prematurely exit.
5792
              */
5793
              state|=EscapeState;
5794
              state|=ExitState;
5795
              break;
5796
            }
5797
            default:
5798
              break;
5799
          }
5800
          (void) XCheckDefineCursor(display,windows->image.id,cursor);
5801
          continue;
5802
        }
5803
      switch (event.type)
5804
      {
5805
        case ButtonPress:
5806
        {
5807
          if (event.xbutton.button != Button1)
5808
            break;
5809
          if (event.xbutton.window != windows->image.id)
5810
            break;
5811
          /*
5812
            exit loop.
5813
          */
5814
          x=event.xbutton.x;
5815
          y=event.xbutton.y;
5816
          state|=ExitState;
5817
          break;
5818
        }
5819
        case ButtonRelease:
5820
          break;
5821
        case Expose:
5822
          break;
5823
        case KeyPress:
5824
        {
5825
          KeySym
5826
            key_symbol;
5827
5828
          if (event.xkey.window != windows->image.id)
5829
            break;
5830
          /*
5831
            Respond to a user key press.
5832
          */
5833
          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5834
            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5835
          switch ((int) key_symbol)
5836
          {
5837
            case XK_Escape:
5838
            case XK_F20:
5839
            {
5840
              /*
5841
                Prematurely exit.
5842
              */
5843
              state|=EscapeState;
5844
              state|=ExitState;
5845
              break;
5846
            }
5847
            case XK_F1:
5848
            case XK_Help:
5849
            {
5850
              XTextViewHelp(display,resource_info,windows,MagickFalse,
5851
                "Help Viewer - Image Rotation",ImageDrawHelp);
5852
              break;
5853
            }
5854
            default:
5855
            {
5856
              (void) XBell(display,0);
5857
              break;
5858
            }
5859
          }
5860
          break;
5861
        }
5862
        case MotionNotify:
5863
        {
5864
          /*
5865
            Map and unmap Info widget as text cursor crosses its boundaries.
5866
          */
5867
          x=event.xmotion.x;
5868
          y=event.xmotion.y;
5869
          if (windows->info.mapped != MagickFalse)
5870
            {
5871
              if ((x < (windows->info.x+(int) windows->info.width)) &&
5872
                  (y < (windows->info.y+(int) windows->info.height)))
5873
                (void) XWithdrawWindow(display,windows->info.id,
5874
                  windows->info.screen);
5875
            }
5876
          else
5877
            if ((x > (windows->info.x+(int) windows->info.width)) ||
5878
                (y > (windows->info.y+(int) windows->info.height)))
5879
              (void) XMapWindow(display,windows->info.id);
5880
          break;
5881
        }
5882
      }
5883
    } while ((state & ExitState) == 0);
5884
    (void) XSelectInput(display,windows->image.id,
5885
      windows->image.attributes.event_mask);
5886
    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5887
    if ((state & EscapeState) != 0)
5888
      break;
5889
    /*
5890
      Draw element as pointer moves until the button is released.
5891
    */
5892
    distance=0;
5893
    degrees=0.0;
5894
    line_info.x1=x;
5895
    line_info.y1=y;
5896
    line_info.x2=x;
5897
    line_info.y2=y;
5898
    rectangle_info.x=(ssize_t) x;
5899
    rectangle_info.y=(ssize_t) y;
5900
    rectangle_info.width=0;
5901
    rectangle_info.height=0;
5902
    number_coordinates=1;
5903
    coordinate_info->x=x;
5904
    coordinate_info->y=y;
5905
    (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5906
    state=DefaultState;
5907
    do
5908
    {
5909
      switch (element)
5910
      {
5911
        case PointElement:
5912
        default:
5913
        {
5914
          if (number_coordinates > 1)
5915
            {
5916
              (void) XDrawLines(display,windows->image.id,
5917
                windows->image.highlight_context,coordinate_info,
5918
                number_coordinates,CoordModeOrigin);
5919
              (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5920
                coordinate_info[number_coordinates-1].x,
5921
                coordinate_info[number_coordinates-1].y);
5922
              XInfoWidget(display,windows,text);
5923
            }
5924
          break;
5925
        }
5926
        case LineElement:
5927
        {
5928
          if (distance > 9)
5929
            {
5930
              /*
5931
                Display angle of the line.
5932
              */
5933
              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5934
                line_info.y1),(double) (line_info.x2-line_info.x1)));
5935
              (void) FormatLocaleString(text,MagickPathExtent," %g",
5936
                (double) degrees);
5937
              XInfoWidget(display,windows,text);
5938
              XHighlightLine(display,windows->image.id,
5939
                windows->image.highlight_context,&line_info);
5940
            }
5941
          else
5942
            if (windows->info.mapped != MagickFalse)
5943
              (void) XWithdrawWindow(display,windows->info.id,
5944
                windows->info.screen);
5945
          break;
5946
        }
5947
        case RectangleElement:
5948
        case FillRectangleElement:
5949
        {
5950
          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5951
            {
5952
              /*
5953
                Display info and draw drawing rectangle.
5954
              */
5955
              (void) FormatLocaleString(text,MagickPathExtent,
5956
                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5957
                (double) rectangle_info.height,(double) rectangle_info.x,
5958
                (double) rectangle_info.y);
5959
              XInfoWidget(display,windows,text);
5960
              XHighlightRectangle(display,windows->image.id,
5961
                windows->image.highlight_context,&rectangle_info);
5962
            }
5963
          else
5964
            if (windows->info.mapped != MagickFalse)
5965
              (void) XWithdrawWindow(display,windows->info.id,
5966
                windows->info.screen);
5967
          break;
5968
        }
5969
        case CircleElement:
5970
        case FillCircleElement:
5971
        case EllipseElement:
5972
        case FillEllipseElement:
5973
        {
5974
          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975
            {
5976
              /*
5977
                Display info and draw drawing rectangle.
5978
              */
5979
              (void) FormatLocaleString(text,MagickPathExtent,
5980
                " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981
                (double) rectangle_info.height,(double) rectangle_info.x,
5982
                (double) rectangle_info.y);
5983
              XInfoWidget(display,windows,text);
5984
              XHighlightEllipse(display,windows->image.id,
5985
                windows->image.highlight_context,&rectangle_info);
5986
            }
5987
          else
5988
            if (windows->info.mapped != MagickFalse)
5989
              (void) XWithdrawWindow(display,windows->info.id,
5990
                windows->info.screen);
5991
          break;
5992
        }
5993
        case PolygonElement:
5994
        case FillPolygonElement:
5995
        {
5996
          if (number_coordinates > 1)
5997
            (void) XDrawLines(display,windows->image.id,
5998
              windows->image.highlight_context,coordinate_info,
5999
              number_coordinates,CoordModeOrigin);
6000
          if (distance > 9)
6001
            {
6002
              /*
6003
                Display angle of the line.
6004
              */
6005
              degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6006
                line_info.y1),(double) (line_info.x2-line_info.x1)));
6007
              (void) FormatLocaleString(text,MagickPathExtent," %g",
6008
                (double) degrees);
6009
              XInfoWidget(display,windows,text);
6010
              XHighlightLine(display,windows->image.id,
6011
                windows->image.highlight_context,&line_info);
6012
            }
6013
          else
6014
            if (windows->info.mapped != MagickFalse)
6015
              (void) XWithdrawWindow(display,windows->info.id,
6016
                windows->info.screen);
6017
          break;
6018
        }
6019
      }
6020
      /*
6021
        Wait for next event.
6022
      */
6023
      XScreenEvent(display,windows,&event,exception);
6024
      switch (element)
6025
      {
6026
        case PointElement:
6027
        default:
6028
        {
6029
          if (number_coordinates > 1)
6030
            (void) XDrawLines(display,windows->image.id,
6031
              windows->image.highlight_context,coordinate_info,
6032
              number_coordinates,CoordModeOrigin);
6033
          break;
6034
        }
6035
        case LineElement:
6036
        {
6037
          if (distance > 9)
6038
            XHighlightLine(display,windows->image.id,
6039
              windows->image.highlight_context,&line_info);
6040
          break;
6041
        }
6042
        case RectangleElement:
6043
        case FillRectangleElement:
6044
        {
6045
          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6046
            XHighlightRectangle(display,windows->image.id,
6047
              windows->image.highlight_context,&rectangle_info);
6048
          break;
6049
        }
6050
        case CircleElement:
6051
        case FillCircleElement:
6052
        case EllipseElement:
6053
        case FillEllipseElement:
6054
        {
6055
          if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6056
            XHighlightEllipse(display,windows->image.id,
6057
              windows->image.highlight_context,&rectangle_info);
6058
          break;
6059
        }
6060
        case PolygonElement:
6061
        case FillPolygonElement:
6062
        {
6063
          if (number_coordinates > 1)
6064
            (void) XDrawLines(display,windows->image.id,
6065
              windows->image.highlight_context,coordinate_info,
6066
              number_coordinates,CoordModeOrigin);
6067
          if (distance > 9)
6068
            XHighlightLine(display,windows->image.id,
6069
              windows->image.highlight_context,&line_info);
6070
          break;
6071
        }
6072
      }
6073
      switch (event.type)
6074
      {
6075
        case ButtonPress:
6076
          break;
6077
        case ButtonRelease:
6078
        {
6079
          /*
6080
            User has committed to element.
6081
          */
6082
          line_info.x2=event.xbutton.x;
6083
          line_info.y2=event.xbutton.y;
6084
          rectangle_info.x=(ssize_t) event.xbutton.x;
6085
          rectangle_info.y=(ssize_t) event.xbutton.y;
6086
          coordinate_info[number_coordinates].x=event.xbutton.x;
6087
          coordinate_info[number_coordinates].y=event.xbutton.y;
6088
          if (((element != PolygonElement) &&
6089
               (element != FillPolygonElement)) || (distance <= 9))
6090
            {
6091
              state|=ExitState;
6092
              break;
6093
            }
6094
          number_coordinates++;
6095
          if (number_coordinates < (int) max_coordinates)
6096
            {
6097
              line_info.x1=event.xbutton.x;
6098
              line_info.y1=event.xbutton.y;
6099
              break;
6100
            }
6101
          max_coordinates<<=1;
6102
          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6103
            max_coordinates,sizeof(*coordinate_info));
6104
          if (coordinate_info == (XPoint *) NULL)
6105
            (void) ThrowMagickException(exception,GetMagickModule(),
6106
              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6107
          break;
6108
        }
6109
        case Expose:
6110
          break;
6111
        case MotionNotify:
6112
        {
6113
          if (event.xmotion.window != windows->image.id)
6114
            break;
6115
          if (element != PointElement)
6116
            {
6117
              line_info.x2=event.xmotion.x;
6118
              line_info.y2=event.xmotion.y;
6119
              rectangle_info.x=(ssize_t) event.xmotion.x;
6120
              rectangle_info.y=(ssize_t) event.xmotion.y;
6121
              break;
6122
            }
6123
          coordinate_info[number_coordinates].x=event.xbutton.x;
6124
          coordinate_info[number_coordinates].y=event.xbutton.y;
6125
          number_coordinates++;
6126
          if (number_coordinates < (int) max_coordinates)
6127
            break;
6128
          max_coordinates<<=1;
6129
          coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6130
            max_coordinates,sizeof(*coordinate_info));
6131
          if (coordinate_info == (XPoint *) NULL)
6132
            (void) ThrowMagickException(exception,GetMagickModule(),
6133
              ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6134
          break;
6135
        }
6136
        default:
6137
          break;
6138
      }
6139
      /*
6140
        Check boundary conditions.
6141
      */
6142
      if (line_info.x2 < 0)
6143
        line_info.x2=0;
6144
      else
6145
        if (line_info.x2 > (int) windows->image.width)
6146
          line_info.x2=(short) windows->image.width;
6147
      if (line_info.y2 < 0)
6148
        line_info.y2=0;
6149
      else
6150
        if (line_info.y2 > (int) windows->image.height)
6151
          line_info.y2=(short) windows->image.height;
6152
      distance=(unsigned int)
6153
        (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6154
         ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6155
      if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6156
          ((state & ExitState) != 0))
6157
        {
6158
          if (rectangle_info.x < 0)
6159
            rectangle_info.x=0;
6160
          else
6161
            if (rectangle_info.x > (ssize_t) windows->image.width)
6162
              rectangle_info.x=(ssize_t) windows->image.width;
6163
          if ((int) rectangle_info.x < x)
6164
            rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6165
          else
6166
            {
6167
              rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6168
              rectangle_info.x=(ssize_t) x;
6169
            }
6170
          if (rectangle_info.y < 0)
6171
            rectangle_info.y=0;
6172
          else
6173
            if (rectangle_info.y > (ssize_t) windows->image.height)
6174
              rectangle_info.y=(ssize_t) windows->image.height;
6175
          if ((int) rectangle_info.y < y)
6176
            rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6177
          else
6178
            {
6179
              rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6180
              rectangle_info.y=(ssize_t) y;
6181
            }
6182
        }
6183
    } while ((state & ExitState) == 0);
6184
    (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6185
    if ((element == PointElement) || (element == PolygonElement) ||
6186
        (element == FillPolygonElement))
6187
      {
6188
        /*
6189
          Determine polygon bounding box.
6190
        */
6191
        rectangle_info.x=(ssize_t) coordinate_info->x;
6192
        rectangle_info.y=(ssize_t) coordinate_info->y;
6193
        x=coordinate_info->x;
6194
        y=coordinate_info->y;
6195
        for (i=1; i < number_coordinates; i++)
6196
        {
6197
          if (coordinate_info[i].x > x)
6198
            x=coordinate_info[i].x;
6199
          if (coordinate_info[i].y > y)
6200
            y=coordinate_info[i].y;
6201
          if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6202
            rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6203
          if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6204
            rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6205
        }
6206
        rectangle_info.width=(size_t) (x-rectangle_info.x);
6207
        rectangle_info.height=(size_t) (y-rectangle_info.y);
6208
        for (i=0; i < number_coordinates; i++)
6209
        {
6210
          coordinate_info[i].x-=rectangle_info.x;
6211
          coordinate_info[i].y-=rectangle_info.y;
6212
        }
6213
      }
6214
    else
6215
      if (distance <= 9)
6216
        continue;
6217
      else
6218
        if ((element == RectangleElement) ||
6219
            (element == CircleElement) || (element == EllipseElement))
6220
          {
6221
            rectangle_info.width--;
6222
            rectangle_info.height--;
6223
          }
6224
    /*
6225
      Drawing is relative to image configuration.
6226
    */
6227
    draw_info.x=(int) rectangle_info.x;
6228
    draw_info.y=(int) rectangle_info.y;
6229
    (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6230
      image,exception);
6231
    width=(unsigned int) (*image)->columns;
6232
    height=(unsigned int) (*image)->rows;
6233
    x=0;
6234
    y=0;
6235
    if (windows->image.crop_geometry != (char *) NULL)
6236
      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6237
    draw_info.x+=windows->image.x-((int) line_width/2);
6238
    if (draw_info.x < 0)
6239
      draw_info.x=0;
6240
    draw_info.x=(int) width*draw_info.x/windows->image.ximage->width;
6241
    draw_info.y+=windows->image.y-((int) line_width/2);
6242
    if (draw_info.y < 0)
6243
      draw_info.y=0;
6244
    draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6245
    draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6246
    if (draw_info.width > (unsigned int) (*image)->columns)
6247
      draw_info.width=(unsigned int) (*image)->columns;
6248
    draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6249
    if (draw_info.height > (unsigned int) (*image)->rows)
6250
      draw_info.height=(unsigned int) (*image)->rows;
6251
    (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6252
      width*draw_info.width/(unsigned int) windows->image.ximage->width,
6253
      height*draw_info.height/(unsigned int) windows->image.ximage->height,
6254
      draw_info.x+x,draw_info.y+y);
6255
    /*
6256
      Initialize drawing attributes.
6257
    */
6258
    draw_info.degrees=0.0;
6259
    draw_info.element=element;
6260
    draw_info.stipple=stipple;
6261
    draw_info.line_width=line_width;
6262
    draw_info.line_info=line_info;
6263
    if (line_info.x1 > (int) (line_width/2))
6264
      draw_info.line_info.x1=(short) line_width/2;
6265
    if (line_info.y1 > (int) (line_width/2))
6266
      draw_info.line_info.y1=(short) line_width/2;
6267
    draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+
6268
      ((int) line_width/2));
6269
    draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+
6270
      ((int) line_width/2));
6271
    if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6272
      {
6273
        draw_info.line_info.x2=(-draw_info.line_info.x2);
6274
        draw_info.line_info.y2=(-draw_info.line_info.y2);
6275
      }
6276
    if (draw_info.line_info.x2 < 0)
6277
      {
6278
        draw_info.line_info.x2=(-draw_info.line_info.x2);
6279
        Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6280
      }
6281
    if (draw_info.line_info.y2 < 0)
6282
      {
6283
        draw_info.line_info.y2=(-draw_info.line_info.y2);
6284
        Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6285
      }
6286
    draw_info.rectangle_info=rectangle_info;
6287
    if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6288
      draw_info.rectangle_info.x=(ssize_t) line_width/2;
6289
    if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6290
      draw_info.rectangle_info.y=(ssize_t) line_width/2;
6291
    draw_info.number_coordinates=(unsigned int) number_coordinates;
6292
    draw_info.coordinate_info=coordinate_info;
6293
    windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6294
    /*
6295
      Draw element on image.
6296
    */
6297
    XSetCursorState(display,windows,MagickTrue);
6298
    XCheckRefreshWindows(display,windows);
6299
    status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6300
    XSetCursorState(display,windows,MagickFalse);
6301
    /*
6302
      Update image colormap and return to image drawing.
6303
    */
6304
    XConfigureImageColormap(display,resource_info,windows,*image,exception);
6305
    (void) XConfigureImage(display,resource_info,windows,*image,exception);
6306
  }
6307
  XSetCursorState(display,windows,MagickFalse);
6308
  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6309
  return(status != 0 ? MagickTrue : MagickFalse);
6310
}
6311

6312
/*
6313
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314
%                                                                             %
6315
%                                                                             %
6316
%                                                                             %
6317
+   X D r a w P a n R e c t a n g l e                                         %
6318
%                                                                             %
6319
%                                                                             %
6320
%                                                                             %
6321
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6322
%
6323
%  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6324
%  displays a zoom image and the rectangle shows which portion of the image is
6325
%  displayed in the Image window.
6326
%
6327
%  The format of the XDrawPanRectangle method is:
6328
%
6329
%      XDrawPanRectangle(Display *display,XWindows *windows)
6330
%
6331
%  A description of each parameter follows:
6332
%
6333
%    o display: Specifies a connection to an X server;  returned from
6334
%      XOpenDisplay.
6335
%
6336
%    o windows: Specifies a pointer to a XWindows structure.
6337
%
6338
*/
6339
static void XDrawPanRectangle(Display *display,XWindows *windows)
6340
{
6341
  double
6342
    scale_factor;
6343
6344
  RectangleInfo
6345
    highlight_info;
6346
6347
  /*
6348
    Determine dimensions of the panning rectangle.
6349
  */
6350
  scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6351
  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6352
  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6353
  scale_factor=(double)
6354
    windows->pan.height/windows->image.ximage->height;
6355
  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6356
  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6357
  /*
6358
    Display the panning rectangle.
6359
  */
6360
  (void) XClearWindow(display,windows->pan.id);
6361
  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6362
    &highlight_info);
6363
}
6364

6365
/*
6366
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6367
%                                                                             %
6368
%                                                                             %
6369
%                                                                             %
6370
+   X I m a g e C a c h e                                                     %
6371
%                                                                             %
6372
%                                                                             %
6373
%                                                                             %
6374
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6375
%
6376
%  XImageCache() handles the creation, manipulation, and destruction of the
6377
%  image cache (undo and redo buffers).
6378
%
6379
%  The format of the XImageCache method is:
6380
%
6381
%      void XImageCache(Display *display,XResourceInfo *resource_info,
6382
%        XWindows *windows,const DisplayCommand command,Image **image,
6383
%        ExceptionInfo *exception)
6384
%
6385
%  A description of each parameter follows:
6386
%
6387
%    o display: Specifies a connection to an X server; returned from
6388
%      XOpenDisplay.
6389
%
6390
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6391
%
6392
%    o windows: Specifies a pointer to a XWindows structure.
6393
%
6394
%    o command: Specifies a command to perform.
6395
%
6396
%    o image: the image;  XImageCache may transform the image and return a new
6397
%      image pointer.
6398
%
6399
%    o exception: return any errors or warnings in this structure.
6400
%
6401
*/
6402
static void XImageCache(Display *display,XResourceInfo *resource_info,
6403
  XWindows *windows,const DisplayCommand command,Image **image,
6404
  ExceptionInfo *exception)
6405
{
6406
  Image
6407
    *cache_image;
6408
6409
  static Image
6410
    *redo_image = (Image *) NULL,
6411
    *undo_image = (Image *) NULL;
6412
6413
  switch (command)
6414
  {
6415
    case FreeBuffersCommand:
6416
    {
6417
      /*
6418
        Free memory from the undo and redo cache.
6419
      */
6420
      while (undo_image != (Image *) NULL)
6421
      {
6422
        cache_image=undo_image;
6423
        undo_image=GetPreviousImageInList(undo_image);
6424
        cache_image->list=DestroyImage(cache_image->list);
6425
        cache_image=DestroyImage(cache_image);
6426
      }
6427
      undo_image=NewImageList();
6428
      if (redo_image != (Image *) NULL)
6429
        redo_image=DestroyImage(redo_image);
6430
      redo_image=NewImageList();
6431
      return;
6432
    }
6433
    case UndoCommand:
6434
    {
6435
      char
6436
        image_geometry[MagickPathExtent];
6437
6438
      /*
6439
        Undo the last image transformation.
6440
      */
6441
      if (undo_image == (Image *) NULL)
6442
        {
6443
          (void) XBell(display,0);
6444
          ThrowXWindowException(ImageError,"NoImagesWereFound",
6445
            (*image)->filename);
6446
          return;
6447
        }
6448
      cache_image=undo_image;
6449
      undo_image=GetPreviousImageInList(undo_image);
6450
      windows->image.window_changes.width=(int) cache_image->columns;
6451
      windows->image.window_changes.height=(int) cache_image->rows;
6452
      (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6453
        windows->image.ximage->width,windows->image.ximage->height);
6454
      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6455
        exception);
6456
      if (windows->image.crop_geometry != (char *) NULL)
6457
        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6458
          windows->image.crop_geometry);
6459
      windows->image.crop_geometry=cache_image->geometry;
6460
      if (redo_image != (Image *) NULL)
6461
        redo_image=DestroyImage(redo_image);
6462
      redo_image=(*image);
6463
      *image=cache_image->list;
6464
      cache_image=DestroyImage(cache_image);
6465
      if (windows->image.orphan != MagickFalse)
6466
        return;
6467
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6468
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6469
      return;
6470
    }
6471
    case CutCommand:
6472
    case PasteCommand:
6473
    case ApplyCommand:
6474
    case HalfSizeCommand:
6475
    case OriginalSizeCommand:
6476
    case DoubleSizeCommand:
6477
    case ResizeCommand:
6478
    case TrimCommand:
6479
    case CropCommand:
6480
    case ChopCommand:
6481
    case FlipCommand:
6482
    case FlopCommand:
6483
    case RotateRightCommand:
6484
    case RotateLeftCommand:
6485
    case RotateCommand:
6486
    case ShearCommand:
6487
    case RollCommand:
6488
    case NegateCommand:
6489
    case ContrastStretchCommand:
6490
    case SigmoidalContrastCommand:
6491
    case NormalizeCommand:
6492
    case EqualizeCommand:
6493
    case HueCommand:
6494
    case SaturationCommand:
6495
    case BrightnessCommand:
6496
    case GammaCommand:
6497
    case SpiffCommand:
6498
    case DullCommand:
6499
    case GrayscaleCommand:
6500
    case MapCommand:
6501
    case QuantizeCommand:
6502
    case DespeckleCommand:
6503
    case EmbossCommand:
6504
    case ReduceNoiseCommand:
6505
    case AddNoiseCommand:
6506
    case SharpenCommand:
6507
    case BlurCommand:
6508
    case ThresholdCommand:
6509
    case EdgeDetectCommand:
6510
    case SpreadCommand:
6511
    case ShadeCommand:
6512
    case RaiseCommand:
6513
    case SegmentCommand:
6514
    case SolarizeCommand:
6515
    case SepiaToneCommand:
6516
    case SwirlCommand:
6517
    case ImplodeCommand:
6518
    case VignetteCommand:
6519
    case WaveCommand:
6520
    case OilPaintCommand:
6521
    case CharcoalDrawCommand:
6522
    case AnnotateCommand:
6523
    case AddBorderCommand:
6524
    case AddFrameCommand:
6525
    case CompositeCommand:
6526
    case CommentCommand:
6527
    case LaunchCommand:
6528
    case RegionOfInterestCommand:
6529
    case SaveToUndoBufferCommand:
6530
    case RedoCommand:
6531
    {
6532
      Image
6533
        *previous_image;
6534
6535
      size_t
6536
        bytes;
6537
6538
      bytes=(*image)->columns*(*image)->rows*sizeof(PixelInfo);
6539
      if (undo_image != (Image *) NULL)
6540
        {
6541
          /*
6542
            Ensure the undo cache has enough memory available.
6543
          */
6544
          previous_image=undo_image;
6545
          while (previous_image != (Image *) NULL)
6546
          {
6547
            bytes+=previous_image->list->columns*previous_image->list->rows*
6548
              sizeof(PixelInfo);
6549
            if (bytes <= (resource_info->undo_cache << 20))
6550
              {
6551
                previous_image=GetPreviousImageInList(previous_image);
6552
                continue;
6553
              }
6554
            bytes-=previous_image->list->columns*previous_image->list->rows*
6555
              sizeof(PixelInfo);
6556
            if (previous_image == undo_image)
6557
              undo_image=NewImageList();
6558
            else
6559
              previous_image->next->previous=NewImageList();
6560
            break;
6561
          }
6562
          while (previous_image != (Image *) NULL)
6563
          {
6564
            /*
6565
              Delete any excess memory from undo cache.
6566
            */
6567
            cache_image=previous_image;
6568
            previous_image=GetPreviousImageInList(previous_image);
6569
            cache_image->list=DestroyImage(cache_image->list);
6570
            cache_image=DestroyImage(cache_image);
6571
          }
6572
        }
6573
      if (bytes > (resource_info->undo_cache << 20))
6574
        break;
6575
      /*
6576
        Save image before transformations are applied.
6577
      */
6578
      cache_image=AcquireImage((ImageInfo *) NULL,exception);
6579
      if (cache_image == (Image *) NULL)
6580
        break;
6581
      XSetCursorState(display,windows,MagickTrue);
6582
      XCheckRefreshWindows(display,windows);
6583
      cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6584
      XSetCursorState(display,windows,MagickFalse);
6585
      if (cache_image->list == (Image *) NULL)
6586
        {
6587
          cache_image=DestroyImage(cache_image);
6588
          break;
6589
        }
6590
      cache_image->columns=(size_t) windows->image.ximage->width;
6591
      cache_image->rows=(size_t) windows->image.ximage->height;
6592
      cache_image->geometry=windows->image.crop_geometry;
6593
      if (windows->image.crop_geometry != (char *) NULL)
6594
        {
6595
          cache_image->geometry=AcquireString((char *) NULL);
6596
          (void) CopyMagickString(cache_image->geometry,
6597
            windows->image.crop_geometry,MagickPathExtent);
6598
        }
6599
      if (undo_image == (Image *) NULL)
6600
        {
6601
          undo_image=cache_image;
6602
          break;
6603
        }
6604
      undo_image->next=cache_image;
6605
      undo_image->next->previous=undo_image;
6606
      undo_image=undo_image->next;
6607
      break;
6608
    }
6609
    default:
6610
      break;
6611
  }
6612
  if (command == RedoCommand)
6613
    {
6614
      /*
6615
        Redo the last image transformation.
6616
      */
6617
      if (redo_image == (Image *) NULL)
6618
        {
6619
          (void) XBell(display,0);
6620
          return;
6621
        }
6622
      windows->image.window_changes.width=(int) redo_image->columns;
6623
      windows->image.window_changes.height=(int) redo_image->rows;
6624
      if (windows->image.crop_geometry != (char *) NULL)
6625
        windows->image.crop_geometry=(char *)
6626
          RelinquishMagickMemory(windows->image.crop_geometry);
6627
      windows->image.crop_geometry=redo_image->geometry;
6628
      *image=DestroyImage(*image);
6629
      *image=redo_image;
6630
      redo_image=NewImageList();
6631
      if (windows->image.orphan != MagickFalse)
6632
        return;
6633
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
6634
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
6635
      return;
6636
    }
6637
  if (command != InfoCommand)
6638
    return;
6639
  /*
6640
    Display image info.
6641
  */
6642
  XSetCursorState(display,windows,MagickTrue);
6643
  XCheckRefreshWindows(display,windows);
6644
  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6645
  XSetCursorState(display,windows,MagickFalse);
6646
}
6647

6648
/*
6649
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6650
%                                                                             %
6651
%                                                                             %
6652
%                                                                             %
6653
+   X I m a g e W i n d o w C o m m a n d                                     %
6654
%                                                                             %
6655
%                                                                             %
6656
%                                                                             %
6657
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6658
%
6659
%  XImageWindowCommand() makes a transform to the image or Image window as
6660
%  specified by a user menu button or keyboard command.
6661
%
6662
%  The format of the XImageWindowCommand method is:
6663
%
6664
%      DisplayCommand XImageWindowCommand(Display *display,
6665
%        XResourceInfo *resource_info,XWindows *windows,
6666
%        const MagickStatusType state,KeySym key_symbol,Image **image,
6667
%        ExceptionInfo *exception)
6668
%
6669
%  A description of each parameter follows:
6670
%
6671
%    o nexus:  Method XImageWindowCommand returns an image when the
6672
%      user chooses 'Open Image' from the command menu.  Otherwise a null
6673
%      image is returned.
6674
%
6675
%    o display: Specifies a connection to an X server; returned from
6676
%      XOpenDisplay.
6677
%
6678
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6679
%
6680
%    o windows: Specifies a pointer to a XWindows structure.
6681
%
6682
%    o state: key mask.
6683
%
6684
%    o key_symbol: Specifies a command to perform.
6685
%
6686
%    o image: the image;  XImageWIndowCommand may transform the image and
6687
%      return a new image pointer.
6688
%
6689
%    o exception: return any errors or warnings in this structure.
6690
%
6691
*/
6692
static DisplayCommand XImageWindowCommand(Display *display,
6693
  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6694
  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6695
{
6696
  static char
6697
    delta[MagickPathExtent+1] = "";
6698
6699
  static const char
6700
    Digits[] = "01234567890";
6701
6702
  static KeySym
6703
    last_symbol = XK_0;
6704
6705
  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6706
    {
6707
      size_t
6708
        length;
6709
6710
      if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6711
        {
6712
          *delta='\0';
6713
          resource_info->quantum=1;
6714
        }
6715
      last_symbol=key_symbol;
6716
      length=strlen(delta);
6717
      if (length < MagickPathExtent)
6718
        {
6719
          delta[length]=Digits[key_symbol-XK_0];
6720
          delta[length+1]='\0';
6721
        }
6722
      resource_info->quantum=StringToLong(delta);
6723
      return(NullCommand);
6724
    }
6725
  last_symbol=key_symbol;
6726
  if (resource_info->immutable)
6727
    {
6728
      /*
6729
        Virtual image window has a restricted command set.
6730
      */
6731
      switch (key_symbol)
6732
      {
6733
        case XK_question:
6734
          return(InfoCommand);
6735
        case XK_p:
6736
        case XK_Print:
6737
          return(PrintCommand);
6738
        case XK_space:
6739
          return(NextCommand);
6740
        case XK_q:
6741
        case XK_Escape:
6742
          return(QuitCommand);
6743
        default:
6744
          break;
6745
      }
6746
      return(NullCommand);
6747
    }
6748
  switch ((int) key_symbol)
6749
  {
6750
    case XK_o:
6751
    {
6752
      if ((state & ControlMask) == 0)
6753
        break;
6754
      return(OpenCommand);
6755
    }
6756
    case XK_space:
6757
      return(NextCommand);
6758
    case XK_BackSpace:
6759
      return(FormerCommand);
6760
    case XK_s:
6761
    {
6762
      if ((state & Mod1Mask) != 0)
6763
        return(SwirlCommand);
6764
      if ((state & ControlMask) == 0)
6765
        return(ShearCommand);
6766
      return(SaveCommand);
6767
    }
6768
    case XK_p:
6769
    case XK_Print:
6770
    {
6771
      if ((state & Mod1Mask) != 0)
6772
        return(OilPaintCommand);
6773
      if ((state & Mod4Mask) != 0)
6774
        return(ColorCommand);
6775
      if ((state & ControlMask) == 0)
6776
        return(NullCommand);
6777
      return(PrintCommand);
6778
    }
6779
    case XK_d:
6780
    {
6781
      if ((state & Mod4Mask) != 0)
6782
        return(DrawCommand);
6783
      if ((state & ControlMask) == 0)
6784
        return(NullCommand);
6785
      return(DeleteCommand);
6786
    }
6787
    case XK_Select:
6788
    {
6789
      if ((state & ControlMask) == 0)
6790
        return(NullCommand);
6791
      return(SelectCommand);
6792
    }
6793
    case XK_n:
6794
    {
6795
      if ((state & ControlMask) == 0)
6796
        return(NullCommand);
6797
      return(NewCommand);
6798
    }
6799
    case XK_q:
6800
    case XK_Escape:
6801
      return(QuitCommand);
6802
    case XK_z:
6803
    case XK_Undo:
6804
    {
6805
      if ((state & ControlMask) == 0)
6806
        return(NullCommand);
6807
      return(UndoCommand);
6808
    }
6809
    case XK_r:
6810
    case XK_Redo:
6811
    {
6812
      if ((state & ControlMask) == 0)
6813
        return(RollCommand);
6814
      return(RedoCommand);
6815
    }
6816
    case XK_x:
6817
    {
6818
      if ((state & ControlMask) == 0)
6819
        return(NullCommand);
6820
      return(CutCommand);
6821
    }
6822
    case XK_c:
6823
    {
6824
      if ((state & Mod1Mask) != 0)
6825
        return(CharcoalDrawCommand);
6826
      if ((state & ControlMask) == 0)
6827
        return(CropCommand);
6828
      return(CopyCommand);
6829
    }
6830
    case XK_v:
6831
    case XK_Insert:
6832
    {
6833
      if ((state & Mod4Mask) != 0)
6834
        return(CompositeCommand);
6835
      if ((state & ControlMask) == 0)
6836
        return(FlipCommand);
6837
      return(PasteCommand);
6838
    }
6839
    case XK_less:
6840
      return(HalfSizeCommand);
6841
    case XK_minus:
6842
      return(OriginalSizeCommand);
6843
    case XK_greater:
6844
      return(DoubleSizeCommand);
6845
    case XK_percent:
6846
      return(ResizeCommand);
6847
    case XK_at:
6848
      return(RefreshCommand);
6849
    case XK_bracketleft:
6850
      return(ChopCommand);
6851
    case XK_h:
6852
      return(FlopCommand);
6853
    case XK_slash:
6854
      return(RotateRightCommand);
6855
    case XK_backslash:
6856
      return(RotateLeftCommand);
6857
    case XK_asterisk:
6858
      return(RotateCommand);
6859
    case XK_t:
6860
      return(TrimCommand);
6861
    case XK_H:
6862
      return(HueCommand);
6863
    case XK_S:
6864
      return(SaturationCommand);
6865
    case XK_L:
6866
      return(BrightnessCommand);
6867
    case XK_G:
6868
      return(GammaCommand);
6869
    case XK_C:
6870
      return(SpiffCommand);
6871
    case XK_Z:
6872
      return(DullCommand);
6873
    case XK_N:
6874
      return(NormalizeCommand);
6875
    case XK_equal:
6876
      return(EqualizeCommand);
6877
    case XK_asciitilde:
6878
      return(NegateCommand);
6879
    case XK_period:
6880
      return(GrayscaleCommand);
6881
    case XK_numbersign:
6882
      return(QuantizeCommand);
6883
    case XK_F2:
6884
      return(DespeckleCommand);
6885
    case XK_F3:
6886
      return(EmbossCommand);
6887
    case XK_F4:
6888
      return(ReduceNoiseCommand);
6889
    case XK_F5:
6890
      return(AddNoiseCommand);
6891
    case XK_F6:
6892
      return(SharpenCommand);
6893
    case XK_F7:
6894
      return(BlurCommand);
6895
    case XK_F8:
6896
      return(ThresholdCommand);
6897
    case XK_F9:
6898
      return(EdgeDetectCommand);
6899
    case XK_F10:
6900
      return(SpreadCommand);
6901
    case XK_F11:
6902
      return(ShadeCommand);
6903
    case XK_F12:
6904
      return(RaiseCommand);
6905
    case XK_F13:
6906
      return(SegmentCommand);
6907
    case XK_i:
6908
    {
6909
      if ((state & Mod1Mask) == 0)
6910
        return(NullCommand);
6911
      return(ImplodeCommand);
6912
    }
6913
    case XK_w:
6914
    {
6915
      if ((state & Mod1Mask) == 0)
6916
        return(NullCommand);
6917
      return(WaveCommand);
6918
    }
6919
    case XK_m:
6920
    {
6921
      if ((state & Mod4Mask) == 0)
6922
        return(NullCommand);
6923
      return(MatteCommand);
6924
    }
6925
    case XK_b:
6926
    {
6927
      if ((state & Mod4Mask) == 0)
6928
        return(NullCommand);
6929
      return(AddBorderCommand);
6930
    }
6931
    case XK_f:
6932
    {
6933
      if ((state & Mod4Mask) == 0)
6934
        return(NullCommand);
6935
      return(AddFrameCommand);
6936
    }
6937
    case XK_exclam:
6938
    {
6939
      if ((state & Mod4Mask) == 0)
6940
        return(NullCommand);
6941
      return(CommentCommand);
6942
    }
6943
    case XK_a:
6944
    {
6945
      if ((state & Mod1Mask) != 0)
6946
        return(ApplyCommand);
6947
      if ((state & Mod4Mask) != 0)
6948
        return(AnnotateCommand);
6949
      if ((state & ControlMask) == 0)
6950
        return(NullCommand);
6951
      return(RegionOfInterestCommand);
6952
    }
6953
    case XK_question:
6954
      return(InfoCommand);
6955
    case XK_plus:
6956
      return(ZoomCommand);
6957
    case XK_P:
6958
    {
6959
      if ((state & ShiftMask) == 0)
6960
        return(NullCommand);
6961
      return(ShowPreviewCommand);
6962
    }
6963
    case XK_Execute:
6964
      return(LaunchCommand);
6965
    case XK_F1:
6966
      return(HelpCommand);
6967
    case XK_Find:
6968
      return(BrowseDocumentationCommand);
6969
    case XK_Menu:
6970
    {
6971
      (void) XMapRaised(display,windows->command.id);
6972
      return(NullCommand);
6973
    }
6974
    case XK_Next:
6975
    case XK_Prior:
6976
    case XK_Home:
6977
    case XK_KP_Home:
6978
    {
6979
      XTranslateImage(display,windows,*image,key_symbol);
6980
      return(NullCommand);
6981
    }
6982
    case XK_Up:
6983
    case XK_KP_Up:
6984
    case XK_Down:
6985
    case XK_KP_Down:
6986
    case XK_Left:
6987
    case XK_KP_Left:
6988
    case XK_Right:
6989
    case XK_KP_Right:
6990
    {
6991
      if ((state & Mod1Mask) != 0)
6992
        {
6993
          RectangleInfo
6994
            crop_info;
6995
6996
          /*
6997
            Trim one pixel from edge of image.
6998
          */
6999
          crop_info.x=0;
7000
          crop_info.y=0;
7001
          crop_info.width=(size_t) windows->image.ximage->width;
7002
          crop_info.height=(size_t) windows->image.ximage->height;
7003
          if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7004
            {
7005
              if (resource_info->quantum >= (int) crop_info.height)
7006
                resource_info->quantum=(int) crop_info.height-1;
7007
              crop_info.height-=(size_t) resource_info->quantum;
7008
            }
7009
          if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7010
            {
7011
              if (resource_info->quantum >= (int) (crop_info.height-(ssize_t) crop_info.y))
7012
                resource_info->quantum=(int) (crop_info.height-(ssize_t) crop_info.y-1);
7013
              crop_info.y+=resource_info->quantum;
7014
              crop_info.height-=(size_t) resource_info->quantum;
7015
            }
7016
          if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7017
            {
7018
              if (resource_info->quantum >= (int) crop_info.width)
7019
                resource_info->quantum=(int) crop_info.width-1;
7020
              crop_info.width-=(size_t) resource_info->quantum;
7021
            }
7022
          if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7023
            {
7024
              if (resource_info->quantum >= (int) (crop_info.width-(ssize_t) crop_info.x))
7025
                resource_info->quantum=(int) (crop_info.width-(ssize_t) crop_info.x-1);
7026
              crop_info.x+=resource_info->quantum;
7027
              crop_info.width-=(size_t) resource_info->quantum;
7028
            }
7029
          if ((windows->image.x+(int) windows->image.width) > (int) crop_info.width)
7030
            windows->image.x=(int) (crop_info.width-windows->image.width);
7031
          if ((windows->image.y+(int) windows->image.height) > (int) crop_info.height)
7032
            windows->image.y=(int) (crop_info.height-windows->image.height);
7033
          XSetCropGeometry(display,windows,&crop_info,*image);
7034
          windows->image.window_changes.width=(int) crop_info.width;
7035
          windows->image.window_changes.height=(int) crop_info.height;
7036
          (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7037
          (void) XConfigureImage(display,resource_info,windows,*image,
7038
            exception);
7039
          return(NullCommand);
7040
        }
7041
      XTranslateImage(display,windows,*image,key_symbol);
7042
      return(NullCommand);
7043
    }
7044
    default:
7045
      return(NullCommand);
7046
  }
7047
  return(NullCommand);
7048
}
7049

7050
/*
7051
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7052
%                                                                             %
7053
%                                                                             %
7054
%                                                                             %
7055
+   X M a g i c k C o m m a n d                                               %
7056
%                                                                             %
7057
%                                                                             %
7058
%                                                                             %
7059
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7060
%
7061
%  XMagickCommand() makes a transform to the image or Image window as
7062
%  specified by a user menu button or keyboard command.
7063
%
7064
%  The format of the XMagickCommand method is:
7065
%
7066
%      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7067
%        XWindows *windows,const DisplayCommand command,Image **image,
7068
%        ExceptionInfo *exception)
7069
%
7070
%  A description of each parameter follows:
7071
%
7072
%    o display: Specifies a connection to an X server; returned from
7073
%      XOpenDisplay.
7074
%
7075
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7076
%
7077
%    o windows: Specifies a pointer to a XWindows structure.
7078
%
7079
%    o command: Specifies a command to perform.
7080
%
7081
%    o image: the image;  XMagickCommand may transform the image and return a
7082
%      new image pointer.
7083
%
7084
%    o exception: return any errors or warnings in this structure.
7085
%
7086
*/
7087
static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7088
  XWindows *windows,const DisplayCommand command,Image **image,
7089
  ExceptionInfo *exception)
7090
{
7091
  char
7092
    filename[MagickPathExtent],
7093
    geometry[MagickPathExtent],
7094
    modulate_factors[MagickPathExtent];
7095
7096
  GeometryInfo
7097
    geometry_info;
7098
7099
  Image
7100
    *nexus;
7101
7102
  ImageInfo
7103
    *image_info;
7104
7105
  int
7106
    x,
7107
    y;
7108
7109
  MagickStatusType
7110
    flags,
7111
    status;
7112
7113
  QuantizeInfo
7114
    quantize_info;
7115
7116
  RectangleInfo
7117
    page_geometry;
7118
7119
  int
7120
    i;
7121
7122
  static char
7123
    color[MagickPathExtent] = "gray";
7124
7125
  unsigned int
7126
    height,
7127
    width;
7128
7129
  /*
7130
    Process user command.
7131
  */
7132
  XCheckRefreshWindows(display,windows);
7133
  XImageCache(display,resource_info,windows,command,image,exception);
7134
  nexus=NewImageList();
7135
  windows->image.window_changes.width=windows->image.ximage->width;
7136
  windows->image.window_changes.height=windows->image.ximage->height;
7137
  image_info=CloneImageInfo(resource_info->image_info);
7138
  SetGeometryInfo(&geometry_info);
7139
  GetQuantizeInfo(&quantize_info);
7140
  switch (command)
7141
  {
7142
    case OpenCommand:
7143
    {
7144
      /*
7145
        Load image.
7146
      */
7147
      nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7148
      break;
7149
    }
7150
    case NextCommand:
7151
    {
7152
      /*
7153
        Display next image.
7154
      */
7155
      for (i=0; i < resource_info->quantum; i++)
7156
        XClientMessage(display,windows->image.id,windows->im_protocols,
7157
          windows->im_next_image,CurrentTime);
7158
      break;
7159
    }
7160
    case FormerCommand:
7161
    {
7162
      /*
7163
        Display former image.
7164
      */
7165
      for (i=0; i < resource_info->quantum; i++)
7166
        XClientMessage(display,windows->image.id,windows->im_protocols,
7167
          windows->im_former_image,CurrentTime);
7168
      break;
7169
    }
7170
    case SelectCommand:
7171
    {
7172
      int
7173
        status;
7174
7175
      /*
7176
        Select image.
7177
      */
7178
      if (*resource_info->home_directory == '\0')
7179
        (void) CopyMagickString(resource_info->home_directory,".",
7180
          MagickPathExtent);
7181
      status=chdir(resource_info->home_directory);
7182
      if (status == -1)
7183
        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7184
          "UnableToOpenFile","%s",resource_info->home_directory);
7185
      nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7186
      break;
7187
    }
7188
    case SaveCommand:
7189
    {
7190
      /*
7191
        Save image.
7192
      */
7193
      status=XSaveImage(display,resource_info,windows,*image,exception);
7194
      if (status == MagickFalse)
7195
        {
7196
          char
7197
            message[MagickPathExtent];
7198
7199
          (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7200
            exception->reason != (char *) NULL ? exception->reason : "",
7201
            exception->description != (char *) NULL ? exception->description :
7202
            "");
7203
          XNoticeWidget(display,windows,"Unable to save file:",message);
7204
          break;
7205
        }
7206
      break;
7207
    }
7208
    case PrintCommand:
7209
    {
7210
      /*
7211
        Print image.
7212
      */
7213
      status=XPrintImage(display,resource_info,windows,*image,exception);
7214
      if (status == MagickFalse)
7215
        {
7216
          char
7217
            message[MagickPathExtent];
7218
7219
          (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7220
            exception->reason != (char *) NULL ? exception->reason : "",
7221
            exception->description != (char *) NULL ? exception->description :
7222
            "");
7223
          XNoticeWidget(display,windows,"Unable to print file:",message);
7224
          break;
7225
        }
7226
      break;
7227
    }
7228
    case DeleteCommand:
7229
    {
7230
      static char
7231
        filename[MagickPathExtent] = "\0";
7232
7233
      /*
7234
        Delete image file.
7235
      */
7236
      XFileBrowserWidget(display,windows,"Delete",filename);
7237
      if (*filename == '\0')
7238
        break;
7239
      status=ShredFile(filename);
7240
      if (remove_utf8(filename) < 0)
7241
        status=MagickTrue;
7242
      if (status != MagickFalse)
7243
        XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7244
      break;
7245
    }
7246
    case NewCommand:
7247
    {
7248
      int
7249
        status;
7250
7251
      static char
7252
        color[MagickPathExtent] = "gray",
7253
        geometry[MagickPathExtent] = "640x480";
7254
7255
      static const char
7256
        *format = "gradient";
7257
7258
      /*
7259
        Query user for canvas geometry.
7260
      */
7261
      status=XDialogWidget(display,windows,"New","Enter image geometry:",
7262
        geometry);
7263
      if (*geometry == '\0')
7264
        break;
7265
      if (status == 0)
7266
        format="xc";
7267
      XColorBrowserWidget(display,windows,"Select",color);
7268
      if (*color == '\0')
7269
        break;
7270
      /*
7271
        Create canvas.
7272
      */
7273
      (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7274
        "%s:%s",format,color);
7275
      (void) CloneString(&image_info->size,geometry);
7276
      nexus=ReadImage(image_info,exception);
7277
      CatchException(exception);
7278
      XClientMessage(display,windows->image.id,windows->im_protocols,
7279
        windows->im_next_image,CurrentTime);
7280
      break;
7281
    }
7282
    case VisualDirectoryCommand:
7283
    {
7284
      /*
7285
        Visual Image directory.
7286
      */
7287
      nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7288
      break;
7289
    }
7290
    case QuitCommand:
7291
    {
7292
      /*
7293
        exit program.
7294
      */
7295
      if (resource_info->confirm_exit == MagickFalse)
7296
        XClientMessage(display,windows->image.id,windows->im_protocols,
7297
          windows->im_exit,CurrentTime);
7298
      else
7299
        {
7300
          int
7301
            status;
7302
7303
          /*
7304
            Confirm program exit.
7305
          */
7306
          status=XConfirmWidget(display,windows,"Do you really want to exit",
7307
            resource_info->client_name);
7308
          if (status > 0)
7309
            XClientMessage(display,windows->image.id,windows->im_protocols,
7310
              windows->im_exit,CurrentTime);
7311
        }
7312
      break;
7313
    }
7314
    case CutCommand:
7315
    {
7316
      /*
7317
        Cut image.
7318
      */
7319
      (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7320
      break;
7321
    }
7322
    case CopyCommand:
7323
    {
7324
      /*
7325
        Copy image.
7326
      */
7327
      (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7328
        exception);
7329
      break;
7330
    }
7331
    case PasteCommand:
7332
    {
7333
      /*
7334
        Paste image.
7335
      */
7336
      status=XPasteImage(display,resource_info,windows,*image,exception);
7337
      if (status == MagickFalse)
7338
        {
7339
          XNoticeWidget(display,windows,"Unable to paste X image",
7340
            (*image)->filename);
7341
          break;
7342
        }
7343
      break;
7344
    }
7345
    case HalfSizeCommand:
7346
    {
7347
      /*
7348
        Half image size.
7349
      */
7350
      windows->image.window_changes.width=windows->image.ximage->width/2;
7351
      windows->image.window_changes.height=windows->image.ximage->height/2;
7352
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7353
      break;
7354
    }
7355
    case OriginalSizeCommand:
7356
    {
7357
      /*
7358
        Original image size.
7359
      */
7360
      windows->image.window_changes.width=(int) (*image)->columns;
7361
      windows->image.window_changes.height=(int) (*image)->rows;
7362
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7363
      break;
7364
    }
7365
    case DoubleSizeCommand:
7366
    {
7367
      /*
7368
        Double the image size.
7369
      */
7370
      windows->image.window_changes.width=windows->image.ximage->width << 1;
7371
      windows->image.window_changes.height=windows->image.ximage->height << 1;
7372
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7373
      break;
7374
    }
7375
    case ResizeCommand:
7376
    {
7377
      int
7378
        status;
7379
7380
      size_t
7381
        height,
7382
        width;
7383
7384
      ssize_t
7385
        x,
7386
        y;
7387
7388
      /*
7389
        Resize image.
7390
      */
7391
      width=(size_t) windows->image.ximage->width;
7392
      height=(size_t) windows->image.ximage->height;
7393
      x=0;
7394
      y=0;
7395
      (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7396
        (double) width,(double) height);
7397
      status=XDialogWidget(display,windows,"Resize",
7398
        "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7399
      if (*geometry == '\0')
7400
        break;
7401
      if (status == 0)
7402
        (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7403
      (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7404
      windows->image.window_changes.width=(int) width;
7405
      windows->image.window_changes.height=(int) height;
7406
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7407
      break;
7408
    }
7409
    case ApplyCommand:
7410
    {
7411
      char
7412
        image_geometry[MagickPathExtent];
7413
7414
      if ((windows->image.crop_geometry == (char *) NULL) &&
7415
          ((int) (*image)->columns == windows->image.ximage->width) &&
7416
          ((int) (*image)->rows == windows->image.ximage->height))
7417
        break;
7418
      /*
7419
        Apply size transforms to image.
7420
      */
7421
      XSetCursorState(display,windows,MagickTrue);
7422
      XCheckRefreshWindows(display,windows);
7423
      /*
7424
        Crop and/or scale displayed image.
7425
      */
7426
      (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7427
        windows->image.ximage->width,windows->image.ximage->height);
7428
      (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7429
        exception);
7430
      if (windows->image.crop_geometry != (char *) NULL)
7431
        windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7432
          windows->image.crop_geometry);
7433
      windows->image.x=0;
7434
      windows->image.y=0;
7435
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7436
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7437
      break;
7438
    }
7439
    case RefreshCommand:
7440
    {
7441
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7442
      break;
7443
    }
7444
    case RestoreCommand:
7445
    {
7446
      /*
7447
        Restore Image window to its original size.
7448
      */
7449
      if ((windows->image.width == (unsigned int) (*image)->columns) &&
7450
          (windows->image.height == (unsigned int) (*image)->rows) &&
7451
          (windows->image.crop_geometry == (char *) NULL))
7452
        {
7453
          (void) XBell(display,0);
7454
          break;
7455
        }
7456
      windows->image.window_changes.width=(int) (*image)->columns;
7457
      windows->image.window_changes.height=(int) (*image)->rows;
7458
      if (windows->image.crop_geometry != (char *) NULL)
7459
        {
7460
          windows->image.crop_geometry=(char *)
7461
            RelinquishMagickMemory(windows->image.crop_geometry);
7462
          windows->image.crop_geometry=(char *) NULL;
7463
          windows->image.x=0;
7464
          windows->image.y=0;
7465
        }
7466
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7467
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7468
      break;
7469
    }
7470
    case CropCommand:
7471
    {
7472
      /*
7473
        Crop image.
7474
      */
7475
      (void) XCropImage(display,resource_info,windows,*image,CropMode,
7476
        exception);
7477
      break;
7478
    }
7479
    case ChopCommand:
7480
    {
7481
      /*
7482
        Chop image.
7483
      */
7484
      status=XChopImage(display,resource_info,windows,image,exception);
7485
      if (status == MagickFalse)
7486
        {
7487
          XNoticeWidget(display,windows,"Unable to cut X image",
7488
            (*image)->filename);
7489
          break;
7490
        }
7491
      break;
7492
    }
7493
    case FlopCommand:
7494
    {
7495
      Image
7496
        *flop_image;
7497
7498
      /*
7499
        Flop image scanlines.
7500
      */
7501
      XSetCursorState(display,windows,MagickTrue);
7502
      XCheckRefreshWindows(display,windows);
7503
      flop_image=FlopImage(*image,exception);
7504
      if (flop_image != (Image *) NULL)
7505
        {
7506
          *image=DestroyImage(*image);
7507
          *image=flop_image;
7508
        }
7509
      CatchException(exception);
7510
      XSetCursorState(display,windows,MagickFalse);
7511
      if (windows->image.crop_geometry != (char *) NULL)
7512
        {
7513
          /*
7514
            Flop crop geometry.
7515
          */
7516
          width=(unsigned int) (*image)->columns;
7517
          height=(unsigned int) (*image)->rows;
7518
          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7519
            &width,&height);
7520
          (void) FormatLocaleString(windows->image.crop_geometry,
7521
            MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
7522
            (int) width-x,y);
7523
        }
7524
      if (windows->image.orphan != MagickFalse)
7525
        break;
7526
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7527
      break;
7528
    }
7529
    case FlipCommand:
7530
    {
7531
      Image
7532
        *flip_image;
7533
7534
      /*
7535
        Flip image scanlines.
7536
      */
7537
      XSetCursorState(display,windows,MagickTrue);
7538
      XCheckRefreshWindows(display,windows);
7539
      flip_image=FlipImage(*image,exception);
7540
      if (flip_image != (Image *) NULL)
7541
        {
7542
          *image=DestroyImage(*image);
7543
          *image=flip_image;
7544
        }
7545
      CatchException(exception);
7546
      XSetCursorState(display,windows,MagickFalse);
7547
      if (windows->image.crop_geometry != (char *) NULL)
7548
        {
7549
          /*
7550
            Flip crop geometry.
7551
          */
7552
          width=(unsigned int) (*image)->columns;
7553
          height=(unsigned int) (*image)->rows;
7554
          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7555
            &width,&height);
7556
          (void) FormatLocaleString(windows->image.crop_geometry,
7557
            MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
7558
            (int) height-y);
7559
        }
7560
      if (windows->image.orphan != MagickFalse)
7561
        break;
7562
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7563
      break;
7564
    }
7565
    case RotateRightCommand:
7566
    {
7567
      /*
7568
        Rotate image 90 degrees clockwise.
7569
      */
7570
      status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7571
      if (status == MagickFalse)
7572
        {
7573
          XNoticeWidget(display,windows,"Unable to rotate X image",
7574
            (*image)->filename);
7575
          break;
7576
        }
7577
      break;
7578
    }
7579
    case RotateLeftCommand:
7580
    {
7581
      /*
7582
        Rotate image 90 degrees counter-clockwise.
7583
      */
7584
      status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7585
      if (status == MagickFalse)
7586
        {
7587
          XNoticeWidget(display,windows,"Unable to rotate X image",
7588
            (*image)->filename);
7589
          break;
7590
        }
7591
      break;
7592
    }
7593
    case RotateCommand:
7594
    {
7595
      /*
7596
        Rotate image.
7597
      */
7598
      status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7599
      if (status == MagickFalse)
7600
        {
7601
          XNoticeWidget(display,windows,"Unable to rotate X image",
7602
            (*image)->filename);
7603
          break;
7604
        }
7605
      break;
7606
    }
7607
    case ShearCommand:
7608
    {
7609
      Image
7610
        *shear_image;
7611
7612
      static char
7613
        geometry[MagickPathExtent] = "45.0x45.0";
7614
7615
      /*
7616
        Query user for shear color and geometry.
7617
      */
7618
      XColorBrowserWidget(display,windows,"Select",color);
7619
      if (*color == '\0')
7620
        break;
7621
      (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7622
        geometry);
7623
      if (*geometry == '\0')
7624
        break;
7625
      /*
7626
        Shear image.
7627
      */
7628
      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7629
        exception);
7630
      XSetCursorState(display,windows,MagickTrue);
7631
      XCheckRefreshWindows(display,windows);
7632
      (void) QueryColorCompliance(color,AllCompliance,
7633
        &(*image)->background_color,exception);
7634
      flags=ParseGeometry(geometry,&geometry_info);
7635
      if ((flags & SigmaValue) == 0)
7636
        geometry_info.sigma=geometry_info.rho;
7637
      shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7638
        exception);
7639
      if (shear_image != (Image *) NULL)
7640
        {
7641
          *image=DestroyImage(*image);
7642
          *image=shear_image;
7643
        }
7644
      CatchException(exception);
7645
      XSetCursorState(display,windows,MagickFalse);
7646
      if (windows->image.orphan != MagickFalse)
7647
        break;
7648
      windows->image.window_changes.width=(int) (*image)->columns;
7649
      windows->image.window_changes.height=(int) (*image)->rows;
7650
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7651
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7652
      break;
7653
    }
7654
    case RollCommand:
7655
    {
7656
      Image
7657
        *roll_image;
7658
7659
      static char
7660
        geometry[MagickPathExtent] = "+2+2";
7661
7662
      /*
7663
        Query user for the roll geometry.
7664
      */
7665
      (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7666
        geometry);
7667
      if (*geometry == '\0')
7668
        break;
7669
      /*
7670
        Roll image.
7671
      */
7672
      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7673
        exception);
7674
      XSetCursorState(display,windows,MagickTrue);
7675
      XCheckRefreshWindows(display,windows);
7676
      (void) ParsePageGeometry(*image,geometry,&page_geometry,
7677
        exception);
7678
      roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7679
        exception);
7680
      if (roll_image != (Image *) NULL)
7681
        {
7682
          *image=DestroyImage(*image);
7683
          *image=roll_image;
7684
        }
7685
      CatchException(exception);
7686
      XSetCursorState(display,windows,MagickFalse);
7687
      if (windows->image.orphan != MagickFalse)
7688
        break;
7689
      windows->image.window_changes.width=(int) (*image)->columns;
7690
      windows->image.window_changes.height=(int) (*image)->rows;
7691
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7692
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7693
      break;
7694
    }
7695
    case TrimCommand:
7696
    {
7697
      static char
7698
        fuzz[MagickPathExtent];
7699
7700
      /*
7701
        Query user for the fuzz factor.
7702
      */
7703
      (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7704
        (*image)->fuzz/((double) QuantumRange+1.0));
7705
      (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7706
      if (*fuzz == '\0')
7707
        break;
7708
      (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7709
      /*
7710
        Trim image.
7711
      */
7712
      status=XTrimImage(display,resource_info,windows,*image,exception);
7713
      if (status == MagickFalse)
7714
        {
7715
          XNoticeWidget(display,windows,"Unable to trim X image",
7716
            (*image)->filename);
7717
          break;
7718
        }
7719
      break;
7720
    }
7721
    case HueCommand:
7722
    {
7723
      static char
7724
        hue_percent[MagickPathExtent] = "110";
7725
7726
      /*
7727
        Query user for percent hue change.
7728
      */
7729
      (void) XDialogWidget(display,windows,"Apply",
7730
        "Enter percent change in image hue (0-200):",hue_percent);
7731
      if (*hue_percent == '\0')
7732
        break;
7733
      /*
7734
        Vary the image hue.
7735
      */
7736
      XSetCursorState(display,windows,MagickTrue);
7737
      XCheckRefreshWindows(display,windows);
7738
      (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7739
      (void) ConcatenateMagickString(modulate_factors,hue_percent,
7740
        MagickPathExtent);
7741
      (void) ModulateImage(*image,modulate_factors,exception);
7742
      XSetCursorState(display,windows,MagickFalse);
7743
      if (windows->image.orphan != MagickFalse)
7744
        break;
7745
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7746
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7747
      break;
7748
    }
7749
    case SaturationCommand:
7750
    {
7751
      static char
7752
        saturation_percent[MagickPathExtent] = "110";
7753
7754
      /*
7755
        Query user for percent saturation change.
7756
      */
7757
      (void) XDialogWidget(display,windows,"Apply",
7758
        "Enter percent change in color saturation (0-200):",saturation_percent);
7759
      if (*saturation_percent == '\0')
7760
        break;
7761
      /*
7762
        Vary color saturation.
7763
      */
7764
      XSetCursorState(display,windows,MagickTrue);
7765
      XCheckRefreshWindows(display,windows);
7766
      (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7767
      (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7768
        MagickPathExtent);
7769
      (void) ModulateImage(*image,modulate_factors,exception);
7770
      XSetCursorState(display,windows,MagickFalse);
7771
      if (windows->image.orphan != MagickFalse)
7772
        break;
7773
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7774
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7775
      break;
7776
    }
7777
    case BrightnessCommand:
7778
    {
7779
      static char
7780
        brightness_percent[MagickPathExtent] = "110";
7781
7782
      /*
7783
        Query user for percent brightness change.
7784
      */
7785
      (void) XDialogWidget(display,windows,"Apply",
7786
        "Enter percent change in color brightness (0-200):",brightness_percent);
7787
      if (*brightness_percent == '\0')
7788
        break;
7789
      /*
7790
        Vary the color brightness.
7791
      */
7792
      XSetCursorState(display,windows,MagickTrue);
7793
      XCheckRefreshWindows(display,windows);
7794
      (void) CopyMagickString(modulate_factors,brightness_percent,
7795
        MagickPathExtent);
7796
      (void) ModulateImage(*image,modulate_factors,exception);
7797
      XSetCursorState(display,windows,MagickFalse);
7798
      if (windows->image.orphan != MagickFalse)
7799
        break;
7800
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7801
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7802
      break;
7803
    }
7804
    case GammaCommand:
7805
    {
7806
      static char
7807
        factor[MagickPathExtent] = "1.6";
7808
7809
      /*
7810
        Query user for gamma value.
7811
      */
7812
      (void) XDialogWidget(display,windows,"Gamma",
7813
        "Enter gamma value (e.g. 1.2):",factor);
7814
      if (*factor == '\0')
7815
        break;
7816
      /*
7817
        Gamma correct image.
7818
      */
7819
      XSetCursorState(display,windows,MagickTrue);
7820
      XCheckRefreshWindows(display,windows);
7821
      (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7822
      XSetCursorState(display,windows,MagickFalse);
7823
      if (windows->image.orphan != MagickFalse)
7824
        break;
7825
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7826
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7827
      break;
7828
    }
7829
    case SpiffCommand:
7830
    {
7831
      /*
7832
        Sharpen the image contrast.
7833
      */
7834
      XSetCursorState(display,windows,MagickTrue);
7835
      XCheckRefreshWindows(display,windows);
7836
      (void) ContrastImage(*image,MagickTrue,exception);
7837
      XSetCursorState(display,windows,MagickFalse);
7838
      if (windows->image.orphan != MagickFalse)
7839
        break;
7840
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7841
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7842
      break;
7843
    }
7844
    case DullCommand:
7845
    {
7846
      /*
7847
        Dull the image contrast.
7848
      */
7849
      XSetCursorState(display,windows,MagickTrue);
7850
      XCheckRefreshWindows(display,windows);
7851
      (void) ContrastImage(*image,MagickFalse,exception);
7852
      XSetCursorState(display,windows,MagickFalse);
7853
      if (windows->image.orphan != MagickFalse)
7854
        break;
7855
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7856
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7857
      break;
7858
    }
7859
    case ContrastStretchCommand:
7860
    {
7861
      double
7862
        black_point,
7863
        white_point;
7864
7865
      static char
7866
        levels[MagickPathExtent] = "1%";
7867
7868
      /*
7869
        Query user for gamma value.
7870
      */
7871
      (void) XDialogWidget(display,windows,"Contrast Stretch",
7872
        "Enter black and white points:",levels);
7873
      if (*levels == '\0')
7874
        break;
7875
      /*
7876
        Contrast stretch image.
7877
      */
7878
      XSetCursorState(display,windows,MagickTrue);
7879
      XCheckRefreshWindows(display,windows);
7880
      flags=ParseGeometry(levels,&geometry_info);
7881
      black_point=geometry_info.rho;
7882
      white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7883
      if ((flags & PercentValue) != 0)
7884
        {
7885
          black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7886
          white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7887
        }
7888
      white_point=(double) (*image)->columns*(*image)->rows-white_point;
7889
      (void) ContrastStretchImage(*image,black_point,white_point,
7890
        exception);
7891
      XSetCursorState(display,windows,MagickFalse);
7892
      if (windows->image.orphan != MagickFalse)
7893
        break;
7894
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7895
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7896
      break;
7897
    }
7898
    case SigmoidalContrastCommand:
7899
    {
7900
      GeometryInfo
7901
        geometry_info;
7902
7903
      MagickStatusType
7904
        flags;
7905
7906
      static char
7907
        levels[MagickPathExtent] = "3x50%";
7908
7909
      /*
7910
        Query user for gamma value.
7911
      */
7912
      (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7913
        "Enter contrast and midpoint:",levels);
7914
      if (*levels == '\0')
7915
        break;
7916
      /*
7917
        Contrast stretch image.
7918
      */
7919
      XSetCursorState(display,windows,MagickTrue);
7920
      XCheckRefreshWindows(display,windows);
7921
      flags=ParseGeometry(levels,&geometry_info);
7922
      if ((flags & SigmaValue) == 0)
7923
        geometry_info.sigma=1.0*(double) QuantumRange/2.0;
7924
      if ((flags & PercentValue) != 0)
7925
        geometry_info.sigma=1.0*(double) QuantumRange*geometry_info.sigma/100.0;
7926
      (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7927
        geometry_info.sigma,exception);
7928
      XSetCursorState(display,windows,MagickFalse);
7929
      if (windows->image.orphan != MagickFalse)
7930
        break;
7931
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7932
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7933
      break;
7934
    }
7935
    case NormalizeCommand:
7936
    {
7937
      /*
7938
        Perform histogram normalization on the image.
7939
      */
7940
      XSetCursorState(display,windows,MagickTrue);
7941
      XCheckRefreshWindows(display,windows);
7942
      (void) NormalizeImage(*image,exception);
7943
      XSetCursorState(display,windows,MagickFalse);
7944
      if (windows->image.orphan != MagickFalse)
7945
        break;
7946
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7947
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7948
      break;
7949
    }
7950
    case EqualizeCommand:
7951
    {
7952
      /*
7953
        Perform histogram equalization on the image.
7954
      */
7955
      XSetCursorState(display,windows,MagickTrue);
7956
      XCheckRefreshWindows(display,windows);
7957
      (void) EqualizeImage(*image,exception);
7958
      XSetCursorState(display,windows,MagickFalse);
7959
      if (windows->image.orphan != MagickFalse)
7960
        break;
7961
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7962
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7963
      break;
7964
    }
7965
    case NegateCommand:
7966
    {
7967
      /*
7968
        Negate colors in image.
7969
      */
7970
      XSetCursorState(display,windows,MagickTrue);
7971
      XCheckRefreshWindows(display,windows);
7972
      (void) NegateImage(*image,MagickFalse,exception);
7973
      XSetCursorState(display,windows,MagickFalse);
7974
      if (windows->image.orphan != MagickFalse)
7975
        break;
7976
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7977
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7978
      break;
7979
    }
7980
    case GrayscaleCommand:
7981
    {
7982
      /*
7983
        Convert image to grayscale.
7984
      */
7985
      XSetCursorState(display,windows,MagickTrue);
7986
      XCheckRefreshWindows(display,windows);
7987
      (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7988
        GrayscaleType : GrayscaleAlphaType,exception);
7989
      XSetCursorState(display,windows,MagickFalse);
7990
      if (windows->image.orphan != MagickFalse)
7991
        break;
7992
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
7993
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
7994
      break;
7995
    }
7996
    case MapCommand:
7997
    {
7998
      Image
7999
        *affinity_image;
8000
8001
      static char
8002
        filename[MagickPathExtent] = "\0";
8003
8004
      /*
8005
        Request image file name from user.
8006
      */
8007
      XFileBrowserWidget(display,windows,"Map",filename);
8008
      if (*filename == '\0')
8009
        break;
8010
      /*
8011
        Map image.
8012
      */
8013
      XSetCursorState(display,windows,MagickTrue);
8014
      XCheckRefreshWindows(display,windows);
8015
      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8016
      affinity_image=ReadImage(image_info,exception);
8017
      if (affinity_image != (Image *) NULL)
8018
        {
8019
          (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8020
          affinity_image=DestroyImage(affinity_image);
8021
        }
8022
      CatchException(exception);
8023
      XSetCursorState(display,windows,MagickFalse);
8024
      if (windows->image.orphan != MagickFalse)
8025
        break;
8026
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8027
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8028
      break;
8029
    }
8030
    case QuantizeCommand:
8031
    {
8032
      int
8033
        status;
8034
8035
      static char
8036
        colors[MagickPathExtent] = "256";
8037
8038
      /*
8039
        Query user for maximum number of colors.
8040
      */
8041
      status=XDialogWidget(display,windows,"Quantize",
8042
        "Maximum number of colors:",colors);
8043
      if (*colors == '\0')
8044
        break;
8045
      /*
8046
        Color reduce the image.
8047
      */
8048
      XSetCursorState(display,windows,MagickTrue);
8049
      XCheckRefreshWindows(display,windows);
8050
      quantize_info.number_colors=StringToUnsignedLong(colors);
8051
      quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8052
        NoDitherMethod;
8053
      (void) QuantizeImage(&quantize_info,*image,exception);
8054
      XSetCursorState(display,windows,MagickFalse);
8055
      if (windows->image.orphan != MagickFalse)
8056
        break;
8057
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8058
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8059
      break;
8060
    }
8061
    case DespeckleCommand:
8062
    {
8063
      Image
8064
        *despeckle_image;
8065
8066
      /*
8067
        Despeckle image.
8068
      */
8069
      XSetCursorState(display,windows,MagickTrue);
8070
      XCheckRefreshWindows(display,windows);
8071
      despeckle_image=DespeckleImage(*image,exception);
8072
      if (despeckle_image != (Image *) NULL)
8073
        {
8074
          *image=DestroyImage(*image);
8075
          *image=despeckle_image;
8076
        }
8077
      CatchException(exception);
8078
      XSetCursorState(display,windows,MagickFalse);
8079
      if (windows->image.orphan != MagickFalse)
8080
        break;
8081
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8082
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8083
      break;
8084
    }
8085
    case EmbossCommand:
8086
    {
8087
      Image
8088
        *emboss_image;
8089
8090
      static char
8091
        radius[MagickPathExtent] = "0.0x1.0";
8092
8093
      /*
8094
        Query user for emboss radius.
8095
      */
8096
      (void) XDialogWidget(display,windows,"Emboss",
8097
        "Enter the emboss radius and standard deviation:",radius);
8098
      if (*radius == '\0')
8099
        break;
8100
      /*
8101
        Reduce noise in the image.
8102
      */
8103
      XSetCursorState(display,windows,MagickTrue);
8104
      XCheckRefreshWindows(display,windows);
8105
      flags=ParseGeometry(radius,&geometry_info);
8106
      if ((flags & SigmaValue) == 0)
8107
        geometry_info.sigma=1.0;
8108
      emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8109
        exception);
8110
      if (emboss_image != (Image *) NULL)
8111
        {
8112
          *image=DestroyImage(*image);
8113
          *image=emboss_image;
8114
        }
8115
      CatchException(exception);
8116
      XSetCursorState(display,windows,MagickFalse);
8117
      if (windows->image.orphan != MagickFalse)
8118
        break;
8119
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8120
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8121
      break;
8122
    }
8123
    case ReduceNoiseCommand:
8124
    {
8125
      Image
8126
        *noise_image;
8127
8128
      static char
8129
        radius[MagickPathExtent] = "0";
8130
8131
      /*
8132
        Query user for noise radius.
8133
      */
8134
      (void) XDialogWidget(display,windows,"Reduce Noise",
8135
        "Enter the noise radius:",radius);
8136
      if (*radius == '\0')
8137
        break;
8138
      /*
8139
        Reduce noise in the image.
8140
      */
8141
      XSetCursorState(display,windows,MagickTrue);
8142
      XCheckRefreshWindows(display,windows);
8143
      flags=ParseGeometry(radius,&geometry_info);
8144
      noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8145
        geometry_info.rho,(size_t) geometry_info.rho,exception);
8146
      if (noise_image != (Image *) NULL)
8147
        {
8148
          *image=DestroyImage(*image);
8149
          *image=noise_image;
8150
        }
8151
      CatchException(exception);
8152
      XSetCursorState(display,windows,MagickFalse);
8153
      if (windows->image.orphan != MagickFalse)
8154
        break;
8155
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8156
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8157
      break;
8158
    }
8159
    case AddNoiseCommand:
8160
    {
8161
      char
8162
        **noises;
8163
8164
      Image
8165
        *noise_image;
8166
8167
      static char
8168
        noise_type[MagickPathExtent] = "Gaussian";
8169
8170
      /*
8171
        Add noise to the image.
8172
      */
8173
      noises=GetCommandOptions(MagickNoiseOptions);
8174
      if (noises == (char **) NULL)
8175
        break;
8176
      XListBrowserWidget(display,windows,&windows->widget,
8177
        (const char **) noises,"Add Noise",
8178
        "Select a type of noise to add to your image:",noise_type);
8179
      noises=DestroyStringList(noises);
8180
      if (*noise_type == '\0')
8181
        break;
8182
      XSetCursorState(display,windows,MagickTrue);
8183
      XCheckRefreshWindows(display,windows);
8184
      noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8185
        MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8186
      if (noise_image != (Image *) NULL)
8187
        {
8188
          *image=DestroyImage(*image);
8189
          *image=noise_image;
8190
        }
8191
      CatchException(exception);
8192
      XSetCursorState(display,windows,MagickFalse);
8193
      if (windows->image.orphan != MagickFalse)
8194
        break;
8195
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8196
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8197
      break;
8198
    }
8199
    case SharpenCommand:
8200
    {
8201
      Image
8202
        *sharp_image;
8203
8204
      static char
8205
        radius[MagickPathExtent] = "0.0x1.0";
8206
8207
      /*
8208
        Query user for sharpen radius.
8209
      */
8210
      (void) XDialogWidget(display,windows,"Sharpen",
8211
        "Enter the sharpen radius and standard deviation:",radius);
8212
      if (*radius == '\0')
8213
        break;
8214
      /*
8215
        Sharpen image scanlines.
8216
      */
8217
      XSetCursorState(display,windows,MagickTrue);
8218
      XCheckRefreshWindows(display,windows);
8219
      flags=ParseGeometry(radius,&geometry_info);
8220
      sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8221
        exception);
8222
      if (sharp_image != (Image *) NULL)
8223
        {
8224
          *image=DestroyImage(*image);
8225
          *image=sharp_image;
8226
        }
8227
      CatchException(exception);
8228
      XSetCursorState(display,windows,MagickFalse);
8229
      if (windows->image.orphan != MagickFalse)
8230
        break;
8231
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8232
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8233
      break;
8234
    }
8235
    case BlurCommand:
8236
    {
8237
      Image
8238
        *blur_image;
8239
8240
      static char
8241
        radius[MagickPathExtent] = "0.0x1.0";
8242
8243
      /*
8244
        Query user for blur radius.
8245
      */
8246
      (void) XDialogWidget(display,windows,"Blur",
8247
        "Enter the blur radius and standard deviation:",radius);
8248
      if (*radius == '\0')
8249
        break;
8250
      /*
8251
        Blur an image.
8252
      */
8253
      XSetCursorState(display,windows,MagickTrue);
8254
      XCheckRefreshWindows(display,windows);
8255
      flags=ParseGeometry(radius,&geometry_info);
8256
      blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8257
        exception);
8258
      if (blur_image != (Image *) NULL)
8259
        {
8260
          *image=DestroyImage(*image);
8261
          *image=blur_image;
8262
        }
8263
      CatchException(exception);
8264
      XSetCursorState(display,windows,MagickFalse);
8265
      if (windows->image.orphan != MagickFalse)
8266
        break;
8267
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8268
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8269
      break;
8270
    }
8271
    case ThresholdCommand:
8272
    {
8273
      double
8274
        threshold;
8275
8276
      static char
8277
        factor[MagickPathExtent] = "128";
8278
8279
      /*
8280
        Query user for threshold value.
8281
      */
8282
      (void) XDialogWidget(display,windows,"Threshold",
8283
        "Enter threshold value:",factor);
8284
      if (*factor == '\0')
8285
        break;
8286
      /*
8287
        Gamma correct image.
8288
      */
8289
      XSetCursorState(display,windows,MagickTrue);
8290
      XCheckRefreshWindows(display,windows);
8291
      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8292
      (void) BilevelImage(*image,threshold,exception);
8293
      XSetCursorState(display,windows,MagickFalse);
8294
      if (windows->image.orphan != MagickFalse)
8295
        break;
8296
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8297
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8298
      break;
8299
    }
8300
    case EdgeDetectCommand:
8301
    {
8302
      Image
8303
        *edge_image;
8304
8305
      static char
8306
        radius[MagickPathExtent] = "0";
8307
8308
      /*
8309
        Query user for edge factor.
8310
      */
8311
      (void) XDialogWidget(display,windows,"Detect Edges",
8312
        "Enter the edge detect radius:",radius);
8313
      if (*radius == '\0')
8314
        break;
8315
      /*
8316
        Detect edge in image.
8317
      */
8318
      XSetCursorState(display,windows,MagickTrue);
8319
      XCheckRefreshWindows(display,windows);
8320
      flags=ParseGeometry(radius,&geometry_info);
8321
      edge_image=EdgeImage(*image,geometry_info.rho,exception);
8322
      if (edge_image != (Image *) NULL)
8323
        {
8324
          *image=DestroyImage(*image);
8325
          *image=edge_image;
8326
        }
8327
      CatchException(exception);
8328
      XSetCursorState(display,windows,MagickFalse);
8329
      if (windows->image.orphan != MagickFalse)
8330
        break;
8331
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8332
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8333
      break;
8334
    }
8335
    case SpreadCommand:
8336
    {
8337
      Image
8338
        *spread_image;
8339
8340
      static char
8341
        amount[MagickPathExtent] = "2";
8342
8343
      /*
8344
        Query user for spread amount.
8345
      */
8346
      (void) XDialogWidget(display,windows,"Spread",
8347
        "Enter the displacement amount:",amount);
8348
      if (*amount == '\0')
8349
        break;
8350
      /*
8351
        Displace image pixels by a random amount.
8352
      */
8353
      XSetCursorState(display,windows,MagickTrue);
8354
      XCheckRefreshWindows(display,windows);
8355
      flags=ParseGeometry(amount,&geometry_info);
8356
      spread_image=EdgeImage(*image,geometry_info.rho,exception);
8357
      if (spread_image != (Image *) NULL)
8358
        {
8359
          *image=DestroyImage(*image);
8360
          *image=spread_image;
8361
        }
8362
      CatchException(exception);
8363
      XSetCursorState(display,windows,MagickFalse);
8364
      if (windows->image.orphan != MagickFalse)
8365
        break;
8366
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8367
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8368
      break;
8369
    }
8370
    case ShadeCommand:
8371
    {
8372
      Image
8373
        *shade_image;
8374
8375
      int
8376
        status;
8377
8378
      static char
8379
        geometry[MagickPathExtent] = "30x30";
8380
8381
      /*
8382
        Query user for the shade geometry.
8383
      */
8384
      status=XDialogWidget(display,windows,"Shade",
8385
        "Enter the azimuth and elevation of the light source:",geometry);
8386
      if (*geometry == '\0')
8387
        break;
8388
      /*
8389
        Shade image pixels.
8390
      */
8391
      XSetCursorState(display,windows,MagickTrue);
8392
      XCheckRefreshWindows(display,windows);
8393
      flags=ParseGeometry(geometry,&geometry_info);
8394
      if ((flags & SigmaValue) == 0)
8395
        geometry_info.sigma=1.0;
8396
      shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8397
        geometry_info.rho,geometry_info.sigma,exception);
8398
      if (shade_image != (Image *) NULL)
8399
        {
8400
          *image=DestroyImage(*image);
8401
          *image=shade_image;
8402
        }
8403
      CatchException(exception);
8404
      XSetCursorState(display,windows,MagickFalse);
8405
      if (windows->image.orphan != MagickFalse)
8406
        break;
8407
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8408
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8409
      break;
8410
    }
8411
    case RaiseCommand:
8412
    {
8413
      static char
8414
        bevel_width[MagickPathExtent] = "10";
8415
8416
      /*
8417
        Query user for bevel width.
8418
      */
8419
      (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8420
      if (*bevel_width == '\0')
8421
        break;
8422
      /*
8423
        Raise an image.
8424
      */
8425
      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8426
        exception);
8427
      XSetCursorState(display,windows,MagickTrue);
8428
      XCheckRefreshWindows(display,windows);
8429
      (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8430
        exception);
8431
      (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8432
      XSetCursorState(display,windows,MagickFalse);
8433
      if (windows->image.orphan != MagickFalse)
8434
        break;
8435
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8436
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8437
      break;
8438
    }
8439
    case SegmentCommand:
8440
    {
8441
      static char
8442
        threshold[MagickPathExtent] = "1.0x1.5";
8443
8444
      /*
8445
        Query user for smoothing threshold.
8446
      */
8447
      (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8448
        threshold);
8449
      if (*threshold == '\0')
8450
        break;
8451
      /*
8452
        Segment an image.
8453
      */
8454
      XSetCursorState(display,windows,MagickTrue);
8455
      XCheckRefreshWindows(display,windows);
8456
      flags=ParseGeometry(threshold,&geometry_info);
8457
      if ((flags & SigmaValue) == 0)
8458
        geometry_info.sigma=1.0;
8459
      (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8460
        geometry_info.sigma,exception);
8461
      XSetCursorState(display,windows,MagickFalse);
8462
      if (windows->image.orphan != MagickFalse)
8463
        break;
8464
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8465
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8466
      break;
8467
    }
8468
    case SepiaToneCommand:
8469
    {
8470
      double
8471
        threshold;
8472
8473
      Image
8474
        *sepia_image;
8475
8476
      static char
8477
        factor[MagickPathExtent] = "80%";
8478
8479
      /*
8480
        Query user for sepia-tone factor.
8481
      */
8482
      (void) XDialogWidget(display,windows,"Sepia Tone",
8483
        "Enter the sepia tone factor (0 - 99.9%):",factor);
8484
      if (*factor == '\0')
8485
        break;
8486
      /*
8487
        Sepia tone image pixels.
8488
      */
8489
      XSetCursorState(display,windows,MagickTrue);
8490
      XCheckRefreshWindows(display,windows);
8491
      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8492
      sepia_image=SepiaToneImage(*image,threshold,exception);
8493
      if (sepia_image != (Image *) NULL)
8494
        {
8495
          *image=DestroyImage(*image);
8496
          *image=sepia_image;
8497
        }
8498
      CatchException(exception);
8499
      XSetCursorState(display,windows,MagickFalse);
8500
      if (windows->image.orphan != MagickFalse)
8501
        break;
8502
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8503
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8504
      break;
8505
    }
8506
    case SolarizeCommand:
8507
    {
8508
      double
8509
        threshold;
8510
8511
      static char
8512
        factor[MagickPathExtent] = "60%";
8513
8514
      /*
8515
        Query user for solarize factor.
8516
      */
8517
      (void) XDialogWidget(display,windows,"Solarize",
8518
        "Enter the solarize factor (0 - 99.9%):",factor);
8519
      if (*factor == '\0')
8520
        break;
8521
      /*
8522
        Solarize image pixels.
8523
      */
8524
      XSetCursorState(display,windows,MagickTrue);
8525
      XCheckRefreshWindows(display,windows);
8526
      threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8527
      (void) SolarizeImage(*image,threshold,exception);
8528
      XSetCursorState(display,windows,MagickFalse);
8529
      if (windows->image.orphan != MagickFalse)
8530
        break;
8531
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8532
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8533
      break;
8534
    }
8535
    case SwirlCommand:
8536
    {
8537
      Image
8538
        *swirl_image;
8539
8540
      static char
8541
        degrees[MagickPathExtent] = "60";
8542
8543
      /*
8544
        Query user for swirl angle.
8545
      */
8546
      (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8547
        degrees);
8548
      if (*degrees == '\0')
8549
        break;
8550
      /*
8551
        Swirl image pixels about the center.
8552
      */
8553
      XSetCursorState(display,windows,MagickTrue);
8554
      XCheckRefreshWindows(display,windows);
8555
      flags=ParseGeometry(degrees,&geometry_info);
8556
      swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8557
        exception);
8558
      if (swirl_image != (Image *) NULL)
8559
        {
8560
          *image=DestroyImage(*image);
8561
          *image=swirl_image;
8562
        }
8563
      CatchException(exception);
8564
      XSetCursorState(display,windows,MagickFalse);
8565
      if (windows->image.orphan != MagickFalse)
8566
        break;
8567
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8568
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8569
      break;
8570
    }
8571
    case ImplodeCommand:
8572
    {
8573
      Image
8574
        *implode_image;
8575
8576
      static char
8577
        factor[MagickPathExtent] = "0.3";
8578
8579
      /*
8580
        Query user for implode factor.
8581
      */
8582
      (void) XDialogWidget(display,windows,"Implode",
8583
        "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8584
      if (*factor == '\0')
8585
        break;
8586
      /*
8587
        Implode image pixels about the center.
8588
      */
8589
      XSetCursorState(display,windows,MagickTrue);
8590
      XCheckRefreshWindows(display,windows);
8591
      flags=ParseGeometry(factor,&geometry_info);
8592
      implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8593
        exception);
8594
      if (implode_image != (Image *) NULL)
8595
        {
8596
          *image=DestroyImage(*image);
8597
          *image=implode_image;
8598
        }
8599
      CatchException(exception);
8600
      XSetCursorState(display,windows,MagickFalse);
8601
      if (windows->image.orphan != MagickFalse)
8602
        break;
8603
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8604
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8605
      break;
8606
    }
8607
    case VignetteCommand:
8608
    {
8609
      Image
8610
        *vignette_image;
8611
8612
      static char
8613
        geometry[MagickPathExtent] = "0x20";
8614
8615
      /*
8616
        Query user for the vignette geometry.
8617
      */
8618
      (void) XDialogWidget(display,windows,"Vignette",
8619
        "Enter the radius, sigma, and x and y offsets:",geometry);
8620
      if (*geometry == '\0')
8621
        break;
8622
      /*
8623
        Soften the edges of the image in vignette style
8624
      */
8625
      XSetCursorState(display,windows,MagickTrue);
8626
      XCheckRefreshWindows(display,windows);
8627
      flags=ParseGeometry(geometry,&geometry_info);
8628
      if ((flags & SigmaValue) == 0)
8629
        geometry_info.sigma=1.0;
8630
      if ((flags & XiValue) == 0)
8631
        geometry_info.xi=0.1*(*image)->columns;
8632
      if ((flags & PsiValue) == 0)
8633
        geometry_info.psi=0.1*(*image)->rows;
8634
      vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8635
        ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8636
        exception);
8637
      if (vignette_image != (Image *) NULL)
8638
        {
8639
          *image=DestroyImage(*image);
8640
          *image=vignette_image;
8641
        }
8642
      CatchException(exception);
8643
      XSetCursorState(display,windows,MagickFalse);
8644
      if (windows->image.orphan != MagickFalse)
8645
        break;
8646
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8647
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8648
      break;
8649
    }
8650
    case WaveCommand:
8651
    {
8652
      Image
8653
        *wave_image;
8654
8655
      static char
8656
        geometry[MagickPathExtent] = "25x150";
8657
8658
      /*
8659
        Query user for the wave geometry.
8660
      */
8661
      (void) XDialogWidget(display,windows,"Wave",
8662
        "Enter the amplitude and length of the wave:",geometry);
8663
      if (*geometry == '\0')
8664
        break;
8665
      /*
8666
        Alter an image along a sine wave.
8667
      */
8668
      XSetCursorState(display,windows,MagickTrue);
8669
      XCheckRefreshWindows(display,windows);
8670
      flags=ParseGeometry(geometry,&geometry_info);
8671
      if ((flags & SigmaValue) == 0)
8672
        geometry_info.sigma=1.0;
8673
      wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8674
        (*image)->interpolate,exception);
8675
      if (wave_image != (Image *) NULL)
8676
        {
8677
          *image=DestroyImage(*image);
8678
          *image=wave_image;
8679
        }
8680
      CatchException(exception);
8681
      XSetCursorState(display,windows,MagickFalse);
8682
      if (windows->image.orphan != MagickFalse)
8683
        break;
8684
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8685
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8686
      break;
8687
    }
8688
    case OilPaintCommand:
8689
    {
8690
      Image
8691
        *paint_image;
8692
8693
      static char
8694
        radius[MagickPathExtent] = "0";
8695
8696
      /*
8697
        Query user for circular neighborhood radius.
8698
      */
8699
      (void) XDialogWidget(display,windows,"Oil Paint",
8700
        "Enter the mask radius:",radius);
8701
      if (*radius == '\0')
8702
        break;
8703
      /*
8704
        OilPaint image scanlines.
8705
      */
8706
      XSetCursorState(display,windows,MagickTrue);
8707
      XCheckRefreshWindows(display,windows);
8708
      flags=ParseGeometry(radius,&geometry_info);
8709
      paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8710
        exception);
8711
      if (paint_image != (Image *) NULL)
8712
        {
8713
          *image=DestroyImage(*image);
8714
          *image=paint_image;
8715
        }
8716
      CatchException(exception);
8717
      XSetCursorState(display,windows,MagickFalse);
8718
      if (windows->image.orphan != MagickFalse)
8719
        break;
8720
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8721
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8722
      break;
8723
    }
8724
    case CharcoalDrawCommand:
8725
    {
8726
      Image
8727
        *charcoal_image;
8728
8729
      static char
8730
        radius[MagickPathExtent] = "0x1";
8731
8732
      /*
8733
        Query user for charcoal radius.
8734
      */
8735
      (void) XDialogWidget(display,windows,"Charcoal Draw",
8736
        "Enter the charcoal radius and sigma:",radius);
8737
      if (*radius == '\0')
8738
        break;
8739
      /*
8740
        Charcoal the image.
8741
      */
8742
      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8743
        exception);
8744
      XSetCursorState(display,windows,MagickTrue);
8745
      XCheckRefreshWindows(display,windows);
8746
      flags=ParseGeometry(radius,&geometry_info);
8747
      if ((flags & SigmaValue) == 0)
8748
        geometry_info.sigma=geometry_info.rho;
8749
      charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8750
        exception);
8751
      if (charcoal_image != (Image *) NULL)
8752
        {
8753
          *image=DestroyImage(*image);
8754
          *image=charcoal_image;
8755
        }
8756
      CatchException(exception);
8757
      XSetCursorState(display,windows,MagickFalse);
8758
      if (windows->image.orphan != MagickFalse)
8759
        break;
8760
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8761
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8762
      break;
8763
    }
8764
    case AnnotateCommand:
8765
    {
8766
      /*
8767
        Annotate the image with text.
8768
      */
8769
      status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8770
      if (status == MagickFalse)
8771
        {
8772
          XNoticeWidget(display,windows,"Unable to annotate X image",
8773
            (*image)->filename);
8774
          break;
8775
        }
8776
      break;
8777
    }
8778
    case DrawCommand:
8779
    {
8780
      /*
8781
        Draw image.
8782
      */
8783
      status=XDrawEditImage(display,resource_info,windows,image,exception);
8784
      if (status == MagickFalse)
8785
        {
8786
          XNoticeWidget(display,windows,"Unable to draw on the X image",
8787
            (*image)->filename);
8788
          break;
8789
        }
8790
      break;
8791
    }
8792
    case ColorCommand:
8793
    {
8794
      /*
8795
        Color edit.
8796
      */
8797
      status=XColorEditImage(display,resource_info,windows,image,exception);
8798
      if (status == MagickFalse)
8799
        {
8800
          XNoticeWidget(display,windows,"Unable to pixel edit X image",
8801
            (*image)->filename);
8802
          break;
8803
        }
8804
      break;
8805
    }
8806
    case MatteCommand:
8807
    {
8808
      /*
8809
        Matte edit.
8810
      */
8811
      status=XMatteEditImage(display,resource_info,windows,image,exception);
8812
      if (status == MagickFalse)
8813
        {
8814
          XNoticeWidget(display,windows,"Unable to matte edit X image",
8815
            (*image)->filename);
8816
          break;
8817
        }
8818
      break;
8819
    }
8820
    case CompositeCommand:
8821
    {
8822
      /*
8823
        Composite image.
8824
      */
8825
      status=XCompositeImage(display,resource_info,windows,*image,
8826
        exception);
8827
      if (status == MagickFalse)
8828
        {
8829
          XNoticeWidget(display,windows,"Unable to composite X image",
8830
            (*image)->filename);
8831
          break;
8832
        }
8833
      break;
8834
    }
8835
    case AddBorderCommand:
8836
    {
8837
      Image
8838
        *border_image;
8839
8840
      static char
8841
        geometry[MagickPathExtent] = "6x6";
8842
8843
      /*
8844
        Query user for border color and geometry.
8845
      */
8846
      XColorBrowserWidget(display,windows,"Select",color);
8847
      if (*color == '\0')
8848
        break;
8849
      (void) XDialogWidget(display,windows,"Add Border",
8850
        "Enter border geometry:",geometry);
8851
      if (*geometry == '\0')
8852
        break;
8853
      /*
8854
        Add a border to the image.
8855
      */
8856
      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8857
        exception);
8858
      XSetCursorState(display,windows,MagickTrue);
8859
      XCheckRefreshWindows(display,windows);
8860
      (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8861
        exception);
8862
      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8863
        exception);
8864
      border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8865
        exception);
8866
      if (border_image != (Image *) NULL)
8867
        {
8868
          *image=DestroyImage(*image);
8869
          *image=border_image;
8870
        }
8871
      CatchException(exception);
8872
      XSetCursorState(display,windows,MagickFalse);
8873
      if (windows->image.orphan != MagickFalse)
8874
        break;
8875
      windows->image.window_changes.width=(int) (*image)->columns;
8876
      windows->image.window_changes.height=(int) (*image)->rows;
8877
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8878
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8879
      break;
8880
    }
8881
    case AddFrameCommand:
8882
    {
8883
      FrameInfo
8884
        frame_info;
8885
8886
      Image
8887
        *frame_image;
8888
8889
      static char
8890
        geometry[MagickPathExtent] = "6x6";
8891
8892
      /*
8893
        Query user for frame color and geometry.
8894
      */
8895
      XColorBrowserWidget(display,windows,"Select",color);
8896
      if (*color == '\0')
8897
        break;
8898
      (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8899
        geometry);
8900
      if (*geometry == '\0')
8901
        break;
8902
      /*
8903
        Surround image with an ornamental border.
8904
      */
8905
      (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8906
        exception);
8907
      XSetCursorState(display,windows,MagickTrue);
8908
      XCheckRefreshWindows(display,windows);
8909
      (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8910
        exception);
8911
      (void) ParsePageGeometry(*image,geometry,&page_geometry,
8912
        exception);
8913
      frame_info.width=page_geometry.width;
8914
      frame_info.height=page_geometry.height;
8915
      frame_info.outer_bevel=page_geometry.x;
8916
      frame_info.inner_bevel=page_geometry.y;
8917
      frame_info.x=(ssize_t) frame_info.width;
8918
      frame_info.y=(ssize_t) frame_info.height;
8919
      frame_info.width=(*image)->columns+2*frame_info.width;
8920
      frame_info.height=(*image)->rows+2*frame_info.height;
8921
      frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8922
      if (frame_image != (Image *) NULL)
8923
        {
8924
          *image=DestroyImage(*image);
8925
          *image=frame_image;
8926
        }
8927
      CatchException(exception);
8928
      XSetCursorState(display,windows,MagickFalse);
8929
      if (windows->image.orphan != MagickFalse)
8930
        break;
8931
      windows->image.window_changes.width=(int) (*image)->columns;
8932
      windows->image.window_changes.height=(int) (*image)->rows;
8933
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
8934
      (void) XConfigureImage(display,resource_info,windows,*image,exception);
8935
      break;
8936
    }
8937
    case CommentCommand:
8938
    {
8939
      const char
8940
        *value;
8941
8942
      FILE
8943
        *file;
8944
8945
      int
8946
        unique_file;
8947
8948
      /*
8949
        Edit image comment.
8950
      */
8951
      unique_file=AcquireUniqueFileResource(image_info->filename);
8952
      if (unique_file == -1)
8953
        {
8954
          XNoticeWidget(display,windows,"Unable to edit image comment",
8955
            image_info->filename);
8956
          break;
8957
        }
8958
      value=GetImageProperty(*image,"comment",exception);
8959
      if (value == (char *) NULL)
8960
        unique_file=close_utf8(unique_file)-1;
8961
      else
8962
        {
8963
          const char
8964
            *p;
8965
8966
          file=fdopen(unique_file,"w");
8967
          if (file == (FILE *) NULL)
8968
            {
8969
              XNoticeWidget(display,windows,"Unable to edit image comment",
8970
                image_info->filename);
8971
              break;
8972
            }
8973
          for (p=value; *p != '\0'; p++)
8974
            (void) fputc((int) *p,file);
8975
          (void) fputc('\n',file);
8976
          (void) fclose(file);
8977
        }
8978
      XSetCursorState(display,windows,MagickTrue);
8979
      XCheckRefreshWindows(display,windows);
8980
      status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8981
        exception);
8982
      if (status == MagickFalse)
8983
        XNoticeWidget(display,windows,"Unable to edit image comment",
8984
          (char *) NULL);
8985
      else
8986
        {
8987
          char
8988
            *comment;
8989
8990
          comment=FileToString(image_info->filename,~0UL,exception);
8991
          if (comment != (char *) NULL)
8992
            {
8993
              (void) SetImageProperty(*image,"comment",comment,exception);
8994
              (*image)->taint=MagickTrue;
8995
            }
8996
        }
8997
      (void) RelinquishUniqueFileResource(image_info->filename);
8998
      XSetCursorState(display,windows,MagickFalse);
8999
      break;
9000
    }
9001
    case LaunchCommand:
9002
    {
9003
      /*
9004
        Launch program.
9005
      */
9006
      XSetCursorState(display,windows,MagickTrue);
9007
      XCheckRefreshWindows(display,windows);
9008
      (void) AcquireUniqueFilename(filename);
9009
      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9010
        filename);
9011
      status=WriteImage(image_info,*image,exception);
9012
      if (status == MagickFalse)
9013
        XNoticeWidget(display,windows,"Unable to launch image editor",
9014
          (char *) NULL);
9015
      else
9016
        {
9017
          nexus=ReadImage(resource_info->image_info,exception);
9018
          CatchException(exception);
9019
          XClientMessage(display,windows->image.id,windows->im_protocols,
9020
            windows->im_next_image,CurrentTime);
9021
        }
9022
      (void) RelinquishUniqueFileResource(filename);
9023
      XSetCursorState(display,windows,MagickFalse);
9024
      break;
9025
    }
9026
    case RegionOfInterestCommand:
9027
    {
9028
      /*
9029
        Apply an image processing technique to a region of interest.
9030
      */
9031
      (void) XROIImage(display,resource_info,windows,image,exception);
9032
      break;
9033
    }
9034
    case InfoCommand:
9035
      break;
9036
    case ZoomCommand:
9037
    {
9038
      /*
9039
        Zoom image.
9040
      */
9041
      if (windows->magnify.mapped != MagickFalse)
9042
        (void) XRaiseWindow(display,windows->magnify.id);
9043
      else
9044
        {
9045
          /*
9046
            Make magnify image.
9047
          */
9048
          XSetCursorState(display,windows,MagickTrue);
9049
          (void) XMapRaised(display,windows->magnify.id);
9050
          XSetCursorState(display,windows,MagickFalse);
9051
        }
9052
      break;
9053
    }
9054
    case ShowPreviewCommand:
9055
    {
9056
      char
9057
        **previews;
9058
9059
      Image
9060
        *preview_image;
9061
9062
      PreviewType
9063
        preview;
9064
9065
      static char
9066
        preview_type[MagickPathExtent] = "Gamma";
9067
9068
      /*
9069
        Select preview type from menu.
9070
      */
9071
      previews=GetCommandOptions(MagickPreviewOptions);
9072
      if (previews == (char **) NULL)
9073
        break;
9074
      XListBrowserWidget(display,windows,&windows->widget,
9075
        (const char **) previews,"Preview",
9076
        "Select an enhancement, effect, or F/X:",preview_type);
9077
      previews=DestroyStringList(previews);
9078
      if (*preview_type == '\0')
9079
        break;
9080
      /*
9081
        Show image preview.
9082
      */
9083
      XSetCursorState(display,windows,MagickTrue);
9084
      XCheckRefreshWindows(display,windows);
9085
      preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9086
        MagickFalse,preview_type);
9087
      (void) FormatImageProperty(*image,"group","%.20g",(double)
9088
        windows->image.id);
9089
      (void) DeleteImageProperty(*image,"label");
9090
      (void) SetImageProperty(*image,"label","Preview",exception);
9091
      preview_image=PreviewImage(*image,preview,exception);
9092
      if (preview_image == (Image *) NULL)
9093
        break;
9094
      (void) AcquireUniqueFilename(filename);
9095
      (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9096
        "show:%s",filename);
9097
      status=WriteImage(image_info,preview_image,exception);
9098
      (void) RelinquishUniqueFileResource(filename);
9099
      preview_image=DestroyImage(preview_image);
9100
      if (status == MagickFalse)
9101
        XNoticeWidget(display,windows,"Unable to show image preview",
9102
          (*image)->filename);
9103
      XDelay(display,1500);
9104
      XSetCursorState(display,windows,MagickFalse);
9105
      break;
9106
    }
9107
    case ShowHistogramCommand:
9108
    {
9109
      Image
9110
        *histogram_image;
9111
9112
      /*
9113
        Show image histogram.
9114
      */
9115
      XSetCursorState(display,windows,MagickTrue);
9116
      XCheckRefreshWindows(display,windows);
9117
      (void) DeleteImageProperty(*image,"label");
9118
      (void) FormatImageProperty(*image,"group","%.20g",(double)
9119
        windows->image.id);
9120
      (void) SetImageProperty(*image,"label","Histogram",exception);
9121
      (void) AcquireUniqueFilename(filename);
9122
      (void) FormatLocaleString((*image)->filename,MagickPathExtent,
9123
        "histogram:%s",filename);
9124
      status=WriteImage(image_info,*image,exception);
9125
      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9126
      histogram_image=ReadImage(image_info,exception);
9127
      (void) RelinquishUniqueFileResource(filename);
9128
      if (histogram_image == (Image *) NULL)
9129
        break;
9130
      (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9131
        "show:%s",filename);
9132
      status=WriteImage(image_info,histogram_image,exception);
9133
      histogram_image=DestroyImage(histogram_image);
9134
      if (status == MagickFalse)
9135
        XNoticeWidget(display,windows,"Unable to show histogram",
9136
          (*image)->filename);
9137
      XDelay(display,1500);
9138
      XSetCursorState(display,windows,MagickFalse);
9139
      break;
9140
    }
9141
    case ShowMatteCommand:
9142
    {
9143
      Image
9144
        *matte_image;
9145
9146
      if ((*image)->alpha_trait == UndefinedPixelTrait)
9147
        {
9148
          XNoticeWidget(display,windows,
9149
            "Image does not have any matte information",(*image)->filename);
9150
          break;
9151
        }
9152
      /*
9153
        Show image matte.
9154
      */
9155
      XSetCursorState(display,windows,MagickTrue);
9156
      XCheckRefreshWindows(display,windows);
9157
      (void) FormatImageProperty(*image,"group","%.20g",(double)
9158
        windows->image.id);
9159
      (void) DeleteImageProperty(*image,"label");
9160
      (void) SetImageProperty(*image,"label","Matte",exception);
9161
      (void) AcquireUniqueFilename(filename);
9162
      (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9163
        filename);
9164
      status=WriteImage(image_info,*image,exception);
9165
      (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9166
      matte_image=ReadImage(image_info,exception);
9167
      (void) RelinquishUniqueFileResource(filename);
9168
      if (matte_image == (Image *) NULL)
9169
        break;
9170
      (void) FormatLocaleString(matte_image->filename,MagickPathExtent,
9171
        "show:%s",filename);
9172
      status=WriteImage(image_info,matte_image,exception);
9173
      matte_image=DestroyImage(matte_image);
9174
      if (status == MagickFalse)
9175
        XNoticeWidget(display,windows,"Unable to show matte",
9176
          (*image)->filename);
9177
      XDelay(display,1500);
9178
      XSetCursorState(display,windows,MagickFalse);
9179
      break;
9180
    }
9181
    case BackgroundCommand:
9182
    {
9183
      /*
9184
        Background image.
9185
      */
9186
      status=XBackgroundImage(display,resource_info,windows,image,exception);
9187
      if (status == MagickFalse)
9188
        break;
9189
      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9190
      if (nexus != (Image *) NULL)
9191
        XClientMessage(display,windows->image.id,windows->im_protocols,
9192
          windows->im_next_image,CurrentTime);
9193
      break;
9194
    }
9195
    case SlideShowCommand:
9196
    {
9197
      static char
9198
        delay[MagickPathExtent] = "5";
9199
9200
      /*
9201
        Display next image after pausing.
9202
      */
9203
      (void) XDialogWidget(display,windows,"Slide Show",
9204
        "Pause how many 1/100ths of a second between images:",delay);
9205
      if (*delay == '\0')
9206
        break;
9207
      resource_info->delay=StringToUnsignedLong(delay);
9208
      XClientMessage(display,windows->image.id,windows->im_protocols,
9209
        windows->im_next_image,CurrentTime);
9210
      break;
9211
    }
9212
    case PreferencesCommand:
9213
    {
9214
      /*
9215
        Set user preferences.
9216
      */
9217
      status=XPreferencesWidget(display,resource_info,windows);
9218
      if (status == MagickFalse)
9219
        break;
9220
      nexus=CloneImage(*image,0,0,MagickTrue,exception);
9221
      if (nexus != (Image *) NULL)
9222
        XClientMessage(display,windows->image.id,windows->im_protocols,
9223
          windows->im_next_image,CurrentTime);
9224
      break;
9225
    }
9226
    case HelpCommand:
9227
    {
9228
      /*
9229
        User requested help.
9230
      */
9231
      XTextViewHelp(display,resource_info,windows,MagickFalse,
9232
        "Help Viewer - Display",DisplayHelp);
9233
      break;
9234
    }
9235
    case BrowseDocumentationCommand:
9236
    {
9237
      Atom
9238
        mozilla_atom;
9239
9240
      Window
9241
        mozilla_window,
9242
        root_window;
9243
9244
      /*
9245
        Browse the ImageMagick documentation.
9246
      */
9247
      root_window=XRootWindow(display,XDefaultScreen(display));
9248
      mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9249
      mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9250
      if (mozilla_window != (Window) NULL)
9251
        {
9252
          char
9253
            command[MagickPathExtent];
9254
9255
          /*
9256
            Display documentation using Netscape remote control.
9257
          */
9258
          (void) FormatLocaleString(command,MagickPathExtent,
9259
            "openurl(%s,new-tab)",MagickAuthoritativeURL);
9260
          mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9261
          (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9262
            8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9263
          XSetCursorState(display,windows,MagickFalse);
9264
          break;
9265
        }
9266
      XSetCursorState(display,windows,MagickTrue);
9267
      XCheckRefreshWindows(display,windows);
9268
      status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9269
        exception);
9270
      if (status == MagickFalse)
9271
        XNoticeWidget(display,windows,"Unable to browse documentation",
9272
          (char *) NULL);
9273
      XDelay(display,1500);
9274
      XSetCursorState(display,windows,MagickFalse);
9275
      break;
9276
    }
9277
    case VersionCommand:
9278
    {
9279
      XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9280
        GetMagickCopyright());
9281
      break;
9282
    }
9283
    case SaveToUndoBufferCommand:
9284
      break;
9285
    default:
9286
    {
9287
      (void) XBell(display,0);
9288
      break;
9289
    }
9290
  }
9291
  image_info=DestroyImageInfo(image_info);
9292
  return(nexus);
9293
}
9294

9295
/*
9296
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9297
%                                                                             %
9298
%                                                                             %
9299
%                                                                             %
9300
+   X M a g n i f y I m a g e                                                 %
9301
%                                                                             %
9302
%                                                                             %
9303
%                                                                             %
9304
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9305
%
9306
%  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9307
%  The magnified portion is displayed in a separate window.
9308
%
9309
%  The format of the XMagnifyImage method is:
9310
%
9311
%      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9312
%        ExceptionInfo *exception)
9313
%
9314
%  A description of each parameter follows:
9315
%
9316
%    o display: Specifies a connection to an X server;  returned from
9317
%      XOpenDisplay.
9318
%
9319
%    o windows: Specifies a pointer to a XWindows structure.
9320
%
9321
%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9322
%      the entire image is refreshed.
9323
%
9324
%    o exception: return any errors or warnings in this structure.
9325
%
9326
*/
9327
static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9328
  ExceptionInfo *exception)
9329
{
9330
  char
9331
    text[MagickPathExtent];
9332
9333
  int
9334
    x,
9335
    y;
9336
9337
  size_t
9338
    state;
9339
9340
  /*
9341
    Update magnified image until the mouse button is released.
9342
  */
9343
  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9344
  state=DefaultState;
9345
  x=event->xbutton.x;
9346
  y=event->xbutton.y;
9347
  windows->magnify.x=(int) windows->image.x+x;
9348
  windows->magnify.y=(int) windows->image.y+y;
9349
  do
9350
  {
9351
    /*
9352
      Map and unmap Info widget as text cursor crosses its boundaries.
9353
    */
9354
    if (windows->info.mapped != MagickFalse)
9355
      {
9356
        if ((x < (windows->info.x+(int) windows->info.width)) &&
9357
            (y < (windows->info.y+(int) windows->info.height)))
9358
          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9359
      }
9360
    else
9361
      if ((x > (windows->info.x+(int) windows->info.width)) ||
9362
          (y > (windows->info.y+(int) windows->info.height)))
9363
        (void) XMapWindow(display,windows->info.id);
9364
    if (windows->info.mapped != MagickFalse)
9365
      {
9366
        /*
9367
          Display pointer position.
9368
        */
9369
        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9370
          windows->magnify.x,windows->magnify.y);
9371
        XInfoWidget(display,windows,text);
9372
      }
9373
    /*
9374
      Wait for next event.
9375
    */
9376
    XScreenEvent(display,windows,event,exception);
9377
    switch (event->type)
9378
    {
9379
      case ButtonPress:
9380
        break;
9381
      case ButtonRelease:
9382
      {
9383
        /*
9384
          User has finished magnifying image.
9385
        */
9386
        x=event->xbutton.x;
9387
        y=event->xbutton.y;
9388
        state|=ExitState;
9389
        break;
9390
      }
9391
      case Expose:
9392
        break;
9393
      case MotionNotify:
9394
      {
9395
        x=event->xmotion.x;
9396
        y=event->xmotion.y;
9397
        break;
9398
      }
9399
      default:
9400
        break;
9401
    }
9402
    /*
9403
      Check boundary conditions.
9404
    */
9405
    if (x < 0)
9406
      x=0;
9407
    else
9408
      if (x >= (int) windows->image.width)
9409
        x=(int) windows->image.width-1;
9410
    if (y < 0)
9411
      y=0;
9412
    else
9413
     if (y >= (int) windows->image.height)
9414
       y=(int) windows->image.height-1;
9415
  } while ((state & ExitState) == 0);
9416
  /*
9417
    Display magnified image.
9418
  */
9419
  XSetCursorState(display,windows,MagickFalse);
9420
}
9421

9422
/*
9423
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9424
%                                                                             %
9425
%                                                                             %
9426
%                                                                             %
9427
+   X M a g n i f y W i n d o w C o m m a n d                                 %
9428
%                                                                             %
9429
%                                                                             %
9430
%                                                                             %
9431
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9432
%
9433
%  XMagnifyWindowCommand() moves the image within an Magnify window by one
9434
%  pixel as specified by the key symbol.
9435
%
9436
%  The format of the XMagnifyWindowCommand method is:
9437
%
9438
%      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9439
%        const MagickStatusType state,const KeySym key_symbol,
9440
%        ExceptionInfo *exception)
9441
%
9442
%  A description of each parameter follows:
9443
%
9444
%    o display: Specifies a connection to an X server; returned from
9445
%      XOpenDisplay.
9446
%
9447
%    o windows: Specifies a pointer to a XWindows structure.
9448
%
9449
%    o state: key mask.
9450
%
9451
%    o key_symbol: Specifies a KeySym which indicates which side of the image
9452
%      to trim.
9453
%
9454
%    o exception: return any errors or warnings in this structure.
9455
%
9456
*/
9457
static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9458
  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9459
{
9460
  unsigned int
9461
    quantum;
9462
9463
  /*
9464
    User specified a magnify factor or position.
9465
  */
9466
  quantum=1;
9467
  if ((state & Mod1Mask) != 0)
9468
    quantum=10;
9469
  switch ((int) key_symbol)
9470
  {
9471
    case QuitCommand:
9472
    {
9473
      (void) XWithdrawWindow(display,windows->magnify.id,
9474
        windows->magnify.screen);
9475
      break;
9476
    }
9477
    case XK_Home:
9478
    case XK_KP_Home:
9479
    {
9480
      windows->magnify.x=(int) windows->image.width/2;
9481
      windows->magnify.y=(int) windows->image.height/2;
9482
      break;
9483
    }
9484
    case XK_Left:
9485
    case XK_KP_Left:
9486
    {
9487
      if (windows->magnify.x > 0)
9488
        windows->magnify.x-=(int) quantum;
9489
      break;
9490
    }
9491
    case XK_Up:
9492
    case XK_KP_Up:
9493
    {
9494
      if (windows->magnify.y > 0)
9495
        windows->magnify.y-=(int) quantum;
9496
      break;
9497
    }
9498
    case XK_Right:
9499
    case XK_KP_Right:
9500
    {
9501
      if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9502
        windows->magnify.x+=(int) quantum;
9503
      break;
9504
    }
9505
    case XK_Down:
9506
    case XK_KP_Down:
9507
    {
9508
      if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9509
        windows->magnify.y+=(int) quantum;
9510
      break;
9511
    }
9512
    case XK_0:
9513
    case XK_1:
9514
    case XK_2:
9515
    case XK_3:
9516
    case XK_4:
9517
    case XK_5:
9518
    case XK_6:
9519
    case XK_7:
9520
    case XK_8:
9521
    case XK_9:
9522
    {
9523
      windows->magnify.data=(key_symbol-XK_0);
9524
      break;
9525
    }
9526
    case XK_KP_0:
9527
    case XK_KP_1:
9528
    case XK_KP_2:
9529
    case XK_KP_3:
9530
    case XK_KP_4:
9531
    case XK_KP_5:
9532
    case XK_KP_6:
9533
    case XK_KP_7:
9534
    case XK_KP_8:
9535
    case XK_KP_9:
9536
    {
9537
      windows->magnify.data=(key_symbol-XK_KP_0);
9538
      break;
9539
    }
9540
    default:
9541
      break;
9542
  }
9543
  XMakeMagnifyImage(display,windows,exception);
9544
}
9545

9546
/*
9547
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9548
%                                                                             %
9549
%                                                                             %
9550
%                                                                             %
9551
+   X M a k e P a n I m a g e                                                 %
9552
%                                                                             %
9553
%                                                                             %
9554
%                                                                             %
9555
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9556
%
9557
%  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9558
%  icon window.
9559
%
9560
%  The format of the XMakePanImage method is:
9561
%
9562
%        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9563
%          XWindows *windows,Image *image,ExceptionInfo *exception)
9564
%
9565
%  A description of each parameter follows:
9566
%
9567
%    o display: Specifies a connection to an X server;  returned from
9568
%      XOpenDisplay.
9569
%
9570
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9571
%
9572
%    o windows: Specifies a pointer to a XWindows structure.
9573
%
9574
%    o image: the image.
9575
%
9576
%    o exception: return any errors or warnings in this structure.
9577
%
9578
*/
9579
static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9580
  XWindows *windows,Image *image,ExceptionInfo *exception)
9581
{
9582
  MagickStatusType
9583
    status;
9584
9585
  /*
9586
    Create and display image for panning icon.
9587
  */
9588
  XSetCursorState(display,windows,MagickTrue);
9589
  XCheckRefreshWindows(display,windows);
9590
  windows->pan.x=(int) windows->image.x;
9591
  windows->pan.y=(int) windows->image.y;
9592
  status=XMakeImage(display,resource_info,&windows->pan,image,
9593
    windows->pan.width,windows->pan.height,exception);
9594
  if (status == MagickFalse)
9595
    ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
9596
      image->filename);
9597
  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9598
    windows->pan.pixmap);
9599
  (void) XClearWindow(display,windows->pan.id);
9600
  XDrawPanRectangle(display,windows);
9601
  XSetCursorState(display,windows,MagickFalse);
9602
}
9603

9604
/*
9605
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9606
%                                                                             %
9607
%                                                                             %
9608
%                                                                             %
9609
+   X M a t t a E d i t I m a g e                                             %
9610
%                                                                             %
9611
%                                                                             %
9612
%                                                                             %
9613
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9614
%
9615
%  XMatteEditImage() allows the user to interactively change the Matte channel
9616
%  of an image.  If the image is PseudoClass it is promoted to DirectClass
9617
%  before the matte information is stored.
9618
%
9619
%  The format of the XMatteEditImage method is:
9620
%
9621
%      MagickBooleanType XMatteEditImage(Display *display,
9622
%        XResourceInfo *resource_info,XWindows *windows,Image **image,
9623
%        ExceptionInfo *exception)
9624
%
9625
%  A description of each parameter follows:
9626
%
9627
%    o display: Specifies a connection to an X server;  returned from
9628
%      XOpenDisplay.
9629
%
9630
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9631
%
9632
%    o windows: Specifies a pointer to a XWindows structure.
9633
%
9634
%    o image: the image; returned from ReadImage.
9635
%
9636
%    o exception: return any errors or warnings in this structure.
9637
%
9638
*/
9639
static MagickBooleanType XMatteEditImage(Display *display,
9640
  XResourceInfo *resource_info,XWindows *windows,Image **image,
9641
  ExceptionInfo *exception)
9642
{
9643
  const char
9644
    *const MatteEditMenu[] =
9645
    {
9646
      "Method",
9647
      "Border Color",
9648
      "Fuzz",
9649
      "Matte Value",
9650
      "Undo",
9651
      "Help",
9652
      "Dismiss",
9653
      (char *) NULL
9654
    };
9655
9656
  static char
9657
    matte[MagickPathExtent] = "0";
9658
9659
  static const ModeType
9660
    MatteEditCommands[] =
9661
    {
9662
      MatteEditMethod,
9663
      MatteEditBorderCommand,
9664
      MatteEditFuzzCommand,
9665
      MatteEditValueCommand,
9666
      MatteEditUndoCommand,
9667
      MatteEditHelpCommand,
9668
      MatteEditDismissCommand
9669
    };
9670
9671
  static PaintMethod
9672
    method = PointMethod;
9673
9674
  static XColor
9675
    border_color = { 0, 0, 0, 0, 0, 0 };
9676
9677
  char
9678
    command[MagickPathExtent],
9679
    text[MagickPathExtent];
9680
9681
  Cursor
9682
    cursor;
9683
9684
  int
9685
    entry,
9686
    id,
9687
    x,
9688
    x_offset,
9689
    y,
9690
    y_offset;
9691
9692
  int
9693
    i;
9694
9695
  Quantum
9696
    *q;
9697
9698
  unsigned int
9699
    height,
9700
    width;
9701
9702
  size_t
9703
    state;
9704
9705
  XEvent
9706
    event;
9707
9708
  /*
9709
    Map Command widget.
9710
  */
9711
  (void) CloneString(&windows->command.name,"Matte Edit");
9712
  windows->command.data=4;
9713
  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9714
  (void) XMapRaised(display,windows->command.id);
9715
  XClientMessage(display,windows->image.id,windows->im_protocols,
9716
    windows->im_update_widget,CurrentTime);
9717
  /*
9718
    Make cursor.
9719
  */
9720
  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9721
    resource_info->background_color,resource_info->foreground_color);
9722
  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9723
  /*
9724
    Track pointer until button 1 is pressed.
9725
  */
9726
  XQueryPosition(display,windows->image.id,&x,&y);
9727
  (void) XSelectInput(display,windows->image.id,
9728
    windows->image.attributes.event_mask | PointerMotionMask);
9729
  state=DefaultState;
9730
  do
9731
  {
9732
    if (windows->info.mapped != MagickFalse)
9733
      {
9734
        /*
9735
          Display pointer position.
9736
        */
9737
        (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9738
          x+windows->image.x,y+windows->image.y);
9739
        XInfoWidget(display,windows,text);
9740
      }
9741
    /*
9742
      Wait for next event.
9743
    */
9744
    XScreenEvent(display,windows,&event,exception);
9745
    if (event.xany.window == windows->command.id)
9746
      {
9747
        /*
9748
          Select a command from the Command widget.
9749
        */
9750
        id=XCommandWidget(display,windows,MatteEditMenu,&event);
9751
        if (id < 0)
9752
          {
9753
            (void) XCheckDefineCursor(display,windows->image.id,cursor);
9754
            continue;
9755
          }
9756
        switch (MatteEditCommands[id])
9757
        {
9758
          case MatteEditMethod:
9759
          {
9760
            char
9761
              **methods;
9762
9763
            /*
9764
              Select a method from the pop-up menu.
9765
            */
9766
            methods=GetCommandOptions(MagickMethodOptions);
9767
            if (methods == (char **) NULL)
9768
              break;
9769
            entry=XMenuWidget(display,windows,MatteEditMenu[id],
9770
              (const char **) methods,command);
9771
            if (entry >= 0)
9772
              method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9773
                MagickFalse,methods[entry]);
9774
            methods=DestroyStringList(methods);
9775
            break;
9776
          }
9777
          case MatteEditBorderCommand:
9778
          {
9779
            const char
9780
              *ColorMenu[MaxNumberPens];
9781
9782
            int
9783
              pen_number;
9784
9785
            /*
9786
              Initialize menu selections.
9787
            */
9788
            for (i=0; i < (int) (MaxNumberPens-2); i++)
9789
              ColorMenu[i]=resource_info->pen_colors[i];
9790
            ColorMenu[MaxNumberPens-2]="Browser...";
9791
            ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9792
            /*
9793
              Select a pen color from the pop-up menu.
9794
            */
9795
            pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9796
              (const char **) ColorMenu,command);
9797
            if (pen_number < 0)
9798
              break;
9799
            if (pen_number == (MaxNumberPens-2))
9800
              {
9801
                static char
9802
                  color_name[MagickPathExtent] = "gray";
9803
9804
                /*
9805
                  Select a pen color from a dialog.
9806
                */
9807
                resource_info->pen_colors[pen_number]=color_name;
9808
                XColorBrowserWidget(display,windows,"Select",color_name);
9809
                if (*color_name == '\0')
9810
                  break;
9811
              }
9812
            /*
9813
              Set border color.
9814
            */
9815
            (void) XParseColor(display,windows->map_info->colormap,
9816
              resource_info->pen_colors[pen_number],&border_color);
9817
            break;
9818
          }
9819
          case MatteEditFuzzCommand:
9820
          {
9821
            const char
9822
              *const FuzzMenu[] =
9823
              {
9824
                "0%",
9825
                "2%",
9826
                "5%",
9827
                "10%",
9828
                "15%",
9829
                "Dialog...",
9830
                (char *) NULL,
9831
              };
9832
9833
            static char
9834
              fuzz[MagickPathExtent];
9835
9836
            /*
9837
              Select a command from the pop-up menu.
9838
            */
9839
            entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9840
              command);
9841
            if (entry < 0)
9842
              break;
9843
            if (entry != 5)
9844
              {
9845
                (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9846
                  QuantumRange+1.0);
9847
                break;
9848
              }
9849
            (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9850
            (void) XDialogWidget(display,windows,"Ok",
9851
              "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9852
            if (*fuzz == '\0')
9853
              break;
9854
            (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9855
            (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9856
              1.0);
9857
            break;
9858
          }
9859
          case MatteEditValueCommand:
9860
          {
9861
            const char
9862
              *const MatteMenu[] =
9863
              {
9864
                "Opaque",
9865
                "Transparent",
9866
                "Dialog...",
9867
                (char *) NULL,
9868
              };
9869
9870
            static char
9871
              message[MagickPathExtent];
9872
9873
            /*
9874
              Select a command from the pop-up menu.
9875
            */
9876
            entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9877
              command);
9878
            if (entry < 0)
9879
              break;
9880
            if (entry != 2)
9881
              {
9882
                (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9883
                  (double) OpaqueAlpha);
9884
                if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9885
                  (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9886
                    (double) TransparentAlpha);
9887
                break;
9888
              }
9889
            (void) FormatLocaleString(message,MagickPathExtent,
9890
              "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9891
            (void) XDialogWidget(display,windows,"Matte",message,matte);
9892
            if (*matte == '\0')
9893
              break;
9894
            break;
9895
          }
9896
          case MatteEditUndoCommand:
9897
          {
9898
            (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9899
              image,exception);
9900
            break;
9901
          }
9902
          case MatteEditHelpCommand:
9903
          {
9904
            XTextViewHelp(display,resource_info,windows,MagickFalse,
9905
              "Help Viewer - Matte Edit",ImageMatteEditHelp);
9906
            break;
9907
          }
9908
          case MatteEditDismissCommand:
9909
          {
9910
            /*
9911
              Prematurely exit.
9912
            */
9913
            state|=EscapeState;
9914
            state|=ExitState;
9915
            break;
9916
          }
9917
          default:
9918
            break;
9919
        }
9920
        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9921
        continue;
9922
      }
9923
    switch (event.type)
9924
    {
9925
      case ButtonPress:
9926
      {
9927
        if (event.xbutton.button != Button1)
9928
          break;
9929
        if ((event.xbutton.window != windows->image.id) &&
9930
            (event.xbutton.window != windows->magnify.id))
9931
          break;
9932
        /*
9933
          Update matte data.
9934
        */
9935
        x=event.xbutton.x;
9936
        y=event.xbutton.y;
9937
        (void) XMagickCommand(display,resource_info,windows,
9938
          SaveToUndoBufferCommand,image,exception);
9939
        state|=UpdateConfigurationState;
9940
        break;
9941
      }
9942
      case ButtonRelease:
9943
      {
9944
        if (event.xbutton.button != Button1)
9945
          break;
9946
        if ((event.xbutton.window != windows->image.id) &&
9947
            (event.xbutton.window != windows->magnify.id))
9948
          break;
9949
        /*
9950
          Update colormap information.
9951
        */
9952
        x=event.xbutton.x;
9953
        y=event.xbutton.y;
9954
        XConfigureImageColormap(display,resource_info,windows,*image,exception);
9955
        (void) XConfigureImage(display,resource_info,windows,*image,exception);
9956
        XInfoWidget(display,windows,text);
9957
        (void) XCheckDefineCursor(display,windows->image.id,cursor);
9958
        state&=(unsigned int) (~UpdateConfigurationState);
9959
        break;
9960
      }
9961
      case Expose:
9962
        break;
9963
      case KeyPress:
9964
      {
9965
        char
9966
          command[MagickPathExtent];
9967
9968
        KeySym
9969
          key_symbol;
9970
9971
        if (event.xkey.window == windows->magnify.id)
9972
          {
9973
            Window
9974
              window;
9975
9976
            window=windows->magnify.id;
9977
            while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9978
          }
9979
        if (event.xkey.window != windows->image.id)
9980
          break;
9981
        /*
9982
          Respond to a user key press.
9983
        */
9984
        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9985
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9986
        switch ((int) key_symbol)
9987
        {
9988
          case XK_Escape:
9989
          case XK_F20:
9990
          {
9991
            /*
9992
              Prematurely exit.
9993
            */
9994
            state|=ExitState;
9995
            break;
9996
          }
9997
          case XK_F1:
9998
          case XK_Help:
9999
          {
10000
            XTextViewHelp(display,resource_info,windows,MagickFalse,
10001
              "Help Viewer - Matte Edit",ImageMatteEditHelp);
10002
            break;
10003
          }
10004
          default:
10005
          {
10006
            (void) XBell(display,0);
10007
            break;
10008
          }
10009
        }
10010
        break;
10011
      }
10012
      case MotionNotify:
10013
      {
10014
        /*
10015
          Map and unmap Info widget as cursor crosses its boundaries.
10016
        */
10017
        x=event.xmotion.x;
10018
        y=event.xmotion.y;
10019
        if (windows->info.mapped != MagickFalse)
10020
          {
10021
            if ((x < (windows->info.x+(int) windows->info.width)) &&
10022
                (y < (windows->info.y+(int) windows->info.height)))
10023
              (void) XWithdrawWindow(display,windows->info.id,
10024
                windows->info.screen);
10025
          }
10026
        else
10027
          if ((x > (windows->info.x+(int) windows->info.width)) ||
10028
              (y > (windows->info.y+(int) windows->info.height)))
10029
            (void) XMapWindow(display,windows->info.id);
10030
        break;
10031
      }
10032
      default:
10033
        break;
10034
    }
10035
    if (event.xany.window == windows->magnify.id)
10036
      {
10037
        x=windows->magnify.x-windows->image.x;
10038
        y=windows->magnify.y-windows->image.y;
10039
      }
10040
    x_offset=x;
10041
    y_offset=y;
10042
    if ((state & UpdateConfigurationState) != 0)
10043
      {
10044
        CacheView
10045
          *image_view;
10046
10047
        int
10048
          x,
10049
          y;
10050
10051
        /*
10052
          Matte edit is relative to image configuration.
10053
        */
10054
        (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10055
          MagickTrue);
10056
        XPutPixel(windows->image.ximage,x_offset,y_offset,
10057
          windows->pixel_info->background_color.pixel);
10058
        width=(unsigned int) (*image)->columns;
10059
        height=(unsigned int) (*image)->rows;
10060
        x=0;
10061
        y=0;
10062
        if (windows->image.crop_geometry != (char *) NULL)
10063
          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10064
            &height);
10065
        x_offset=((int) width*(windows->image.x+x_offset)/
10066
          windows->image.ximage->width+x);
10067
        y_offset=((int) height*(windows->image.y+y_offset)/
10068
          windows->image.ximage->height+y);
10069
        if ((x_offset < 0) || (y_offset < 0))
10070
          continue;
10071
        if ((x_offset >= (int) (*image)->columns) ||
10072
            (y_offset >= (int) (*image)->rows))
10073
          continue;
10074
        if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10075
          return(MagickFalse);
10076
        if ((*image)->alpha_trait == UndefinedPixelTrait)
10077
          (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10078
        image_view=AcquireAuthenticCacheView(*image,exception);
10079
        switch (method)
10080
        {
10081
          case PointMethod:
10082
          default:
10083
          {
10084
            /*
10085
              Update matte information using point algorithm.
10086
            */
10087
            q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10088
              (ssize_t) y_offset,1,1,exception);
10089
            if (q == (Quantum *) NULL)
10090
              break;
10091
            SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10092
            (void) SyncCacheViewAuthenticPixels(image_view,exception);
10093
            break;
10094
          }
10095
          case ReplaceMethod:
10096
          {
10097
            PixelInfo
10098
              pixel,
10099
              target;
10100
10101
            /*
10102
              Update matte information using replace algorithm.
10103
            */
10104
            (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10105
              x_offset,(ssize_t) y_offset,&target,exception);
10106
            for (y=0; y < (int) (*image)->rows; y++)
10107
            {
10108
              q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10109
                (*image)->columns,1,exception);
10110
              if (q == (Quantum *) NULL)
10111
                break;
10112
              for (x=0; x < (int) (*image)->columns; x++)
10113
              {
10114
                GetPixelInfoPixel(*image,q,&pixel);
10115
                if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10116
                  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10117
                q+=(ptrdiff_t) GetPixelChannels(*image);
10118
              }
10119
              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10120
                break;
10121
            }
10122
            break;
10123
          }
10124
          case FloodfillMethod:
10125
          case FillToBorderMethod:
10126
          {
10127
            ChannelType
10128
              channel_mask;
10129
10130
            DrawInfo
10131
              *draw_info;
10132
10133
            PixelInfo
10134
              target;
10135
10136
            /*
10137
              Update matte information using floodfill algorithm.
10138
            */
10139
            (void) GetOneVirtualPixelInfo(*image,
10140
              GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10141
              y_offset,&target,exception);
10142
            if (method == FillToBorderMethod)
10143
              {
10144
                target.red=(double) ScaleShortToQuantum(
10145
                  border_color.red);
10146
                target.green=(double) ScaleShortToQuantum(
10147
                  border_color.green);
10148
                target.blue=(double) ScaleShortToQuantum(
10149
                  border_color.blue);
10150
              }
10151
            draw_info=CloneDrawInfo(resource_info->image_info,
10152
              (DrawInfo *) NULL);
10153
            draw_info->fill.alpha=(double) ClampToQuantum(
10154
              StringToDouble(matte,(char **) NULL));
10155
            channel_mask=SetImageChannelMask(*image,AlphaChannel);
10156
            (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10157
              x_offset,(ssize_t) y_offset,
10158
              method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10159
            (void) SetPixelChannelMask(*image,channel_mask);
10160
            draw_info=DestroyDrawInfo(draw_info);
10161
            break;
10162
          }
10163
          case ResetMethod:
10164
          {
10165
            /*
10166
              Update matte information using reset algorithm.
10167
            */
10168
            if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10169
              return(MagickFalse);
10170
            for (y=0; y < (int) (*image)->rows; y++)
10171
            {
10172
              q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10173
                (*image)->columns,1,exception);
10174
              if (q == (Quantum *) NULL)
10175
                break;
10176
              for (x=0; x < (int) (*image)->columns; x++)
10177
              {
10178
                SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10179
                q+=(ptrdiff_t) GetPixelChannels(*image);
10180
              }
10181
              if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10182
                break;
10183
            }
10184
            if (StringToLong(matte) == (long) OpaqueAlpha)
10185
              (*image)->alpha_trait=UndefinedPixelTrait;
10186
            break;
10187
          }
10188
        }
10189
        image_view=DestroyCacheView(image_view);
10190
        state&=(unsigned int) (~UpdateConfigurationState);
10191
      }
10192
  } while ((state & ExitState) == 0);
10193
  (void) XSelectInput(display,windows->image.id,
10194
    windows->image.attributes.event_mask);
10195
  XSetCursorState(display,windows,MagickFalse);
10196
  (void) XFreeCursor(display,cursor);
10197
  return(MagickTrue);
10198
}
10199

10200
/*
10201
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10202
%                                                                             %
10203
%                                                                             %
10204
%                                                                             %
10205
+   X O p e n I m a g e                                                       %
10206
%                                                                             %
10207
%                                                                             %
10208
%                                                                             %
10209
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10210
%
10211
%  XOpenImage() loads an image from a file.
10212
%
10213
%  The format of the XOpenImage method is:
10214
%
10215
%     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10216
%       XWindows *windows,const unsigned int command)
10217
%
10218
%  A description of each parameter follows:
10219
%
10220
%    o display: Specifies a connection to an X server; returned from
10221
%      XOpenDisplay.
10222
%
10223
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10224
%
10225
%    o windows: Specifies a pointer to a XWindows structure.
10226
%
10227
%    o command: A value other than zero indicates that the file is selected
10228
%      from the command line argument list.
10229
%
10230
*/
10231
static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10232
  XWindows *windows,const MagickBooleanType command)
10233
{
10234
  const MagickInfo
10235
    *magick_info;
10236
10237
  ExceptionInfo
10238
    *exception;
10239
10240
  Image
10241
    *nexus;
10242
10243
  ImageInfo
10244
    *image_info;
10245
10246
  static char
10247
    filename[MagickPathExtent] = "\0";
10248
10249
  /*
10250
    Request file name from user.
10251
  */
10252
  if (command == MagickFalse)
10253
    XFileBrowserWidget(display,windows,"Open",filename);
10254
  else
10255
    {
10256
      char
10257
        **filelist,
10258
        **files;
10259
10260
      int
10261
        count,
10262
        status;
10263
10264
      int
10265
        i,
10266
        j;
10267
10268
      /*
10269
        Select next image from the command line.
10270
      */
10271
      status=XGetCommand(display,windows->image.id,&files,&count);
10272
      if (status == 0)
10273
        {
10274
          ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10275
          return((Image *) NULL);
10276
        }
10277
      filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10278
      if (filelist == (char **) NULL)
10279
        {
10280
          ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
10281
            "...");
10282
          (void) XFreeStringList(files);
10283
          return((Image *) NULL);
10284
        }
10285
      j=0;
10286
      for (i=1; i < count; i++)
10287
        if (*files[i] != '-')
10288
          filelist[j++]=files[i];
10289
      filelist[j]=(char *) NULL;
10290
      XListBrowserWidget(display,windows,&windows->widget,
10291
        (const char **) filelist,"Load","Select Image to Load:",filename);
10292
      filelist=(char **) RelinquishMagickMemory(filelist);
10293
      (void) XFreeStringList(files);
10294
    }
10295
  if (*filename == '\0')
10296
    return((Image *) NULL);
10297
  image_info=CloneImageInfo(resource_info->image_info);
10298
  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10299
    (void *) NULL);
10300
  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10301
  exception=AcquireExceptionInfo();
10302
  (void) SetImageInfo(image_info,0,exception);
10303
  if (LocaleCompare(image_info->magick,"X") == 0)
10304
    {
10305
      char
10306
        seconds[MagickPathExtent];
10307
10308
      /*
10309
        User may want to delay the X server screen grab.
10310
      */
10311
      (void) CopyMagickString(seconds,"0",MagickPathExtent);
10312
      (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10313
        seconds);
10314
      if (*seconds == '\0')
10315
        return((Image *) NULL);
10316
      XDelay(display,(size_t) (1000*StringToLong(seconds)));
10317
    }
10318
  magick_info=GetMagickInfo(image_info->magick,exception);
10319
  if ((magick_info != (const MagickInfo *) NULL) &&
10320
      GetMagickRawSupport(magick_info) == MagickTrue)
10321
    {
10322
      char
10323
        geometry[MagickPathExtent];
10324
10325
      /*
10326
        Request image size from the user.
10327
      */
10328
      (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10329
      if (image_info->size != (char *) NULL)
10330
        (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10331
      (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10332
        geometry);
10333
      (void) CloneString(&image_info->size,geometry);
10334
    }
10335
  /*
10336
    Load the image.
10337
  */
10338
  XSetCursorState(display,windows,MagickTrue);
10339
  XCheckRefreshWindows(display,windows);
10340
  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10341
  nexus=ReadImage(image_info,exception);
10342
  CatchException(exception);
10343
  XSetCursorState(display,windows,MagickFalse);
10344
  if (nexus != (Image *) NULL)
10345
    XClientMessage(display,windows->image.id,windows->im_protocols,
10346
      windows->im_next_image,CurrentTime);
10347
  else
10348
    {
10349
      char
10350
        *text,
10351
        **textlist;
10352
10353
      /*
10354
        Unknown image format.
10355
      */
10356
      text=FileToString(filename,~0UL,exception);
10357
      if (text == (char *) NULL)
10358
        return((Image *) NULL);
10359
      textlist=StringToList(text);
10360
      if (textlist != (char **) NULL)
10361
        {
10362
          char
10363
            title[MagickPathExtent];
10364
10365
          int
10366
            i;
10367
10368
          (void) FormatLocaleString(title,MagickPathExtent,
10369
            "Unknown format: %s",filename);
10370
          XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10371
            (const char **) textlist);
10372
          for (i=0; textlist[i] != (char *) NULL; i++)
10373
            textlist[i]=DestroyString(textlist[i]);
10374
          textlist=(char **) RelinquishMagickMemory(textlist);
10375
        }
10376
      text=DestroyString(text);
10377
    }
10378
  exception=DestroyExceptionInfo(exception);
10379
  image_info=DestroyImageInfo(image_info);
10380
  return(nexus);
10381
}
10382

10383
/*
10384
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10385
%                                                                             %
10386
%                                                                             %
10387
%                                                                             %
10388
+   X P a n I m a g e                                                         %
10389
%                                                                             %
10390
%                                                                             %
10391
%                                                                             %
10392
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10393
%
10394
%  XPanImage() pans the image until the mouse button is released.
10395
%
10396
%  The format of the XPanImage method is:
10397
%
10398
%      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10399
%        ExceptionInfo *exception)
10400
%
10401
%  A description of each parameter follows:
10402
%
10403
%    o display: Specifies a connection to an X server;  returned from
10404
%      XOpenDisplay.
10405
%
10406
%    o windows: Specifies a pointer to a XWindows structure.
10407
%
10408
%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10409
%      the entire image is refreshed.
10410
%
10411
%    o exception: return any errors or warnings in this structure.
10412
%
10413
*/
10414
static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10415
  ExceptionInfo *exception)
10416
{
10417
  char
10418
    text[MagickPathExtent];
10419
10420
  Cursor
10421
    cursor;
10422
10423
  double
10424
    x_factor,
10425
    y_factor;
10426
10427
  RectangleInfo
10428
    pan_info;
10429
10430
  size_t
10431
    state;
10432
10433
  /*
10434
    Define cursor.
10435
  */
10436
  if ((windows->image.ximage->width > (int) windows->image.width) &&
10437
      (windows->image.ximage->height > (int) windows->image.height))
10438
    cursor=XCreateFontCursor(display,XC_fleur);
10439
  else
10440
    if (windows->image.ximage->width > (int) windows->image.width)
10441
      cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10442
    else
10443
      if (windows->image.ximage->height > (int) windows->image.height)
10444
        cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10445
      else
10446
        cursor=XCreateFontCursor(display,XC_arrow);
10447
  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10448
  /*
10449
    Pan image as pointer moves until the mouse button is released.
10450
  */
10451
  x_factor=(double) windows->image.ximage->width/windows->pan.width;
10452
  y_factor=(double) windows->image.ximage->height/windows->pan.height;
10453
  pan_info.width=windows->pan.width*windows->image.width/
10454
    (unsigned int) windows->image.ximage->width;
10455
  pan_info.height=windows->pan.height*windows->image.height/
10456
    (unsigned int) windows->image.ximage->height;
10457
  pan_info.x=0;
10458
  pan_info.y=0;
10459
  state=UpdateConfigurationState;
10460
  do
10461
  {
10462
    switch (event->type)
10463
    {
10464
      case ButtonPress:
10465
      {
10466
        /*
10467
          User choose an initial pan location.
10468
        */
10469
        pan_info.x=(ssize_t) event->xbutton.x;
10470
        pan_info.y=(ssize_t) event->xbutton.y;
10471
        state|=UpdateConfigurationState;
10472
        break;
10473
      }
10474
      case ButtonRelease:
10475
      {
10476
        /*
10477
          User has finished panning the image.
10478
        */
10479
        pan_info.x=(ssize_t) event->xbutton.x;
10480
        pan_info.y=(ssize_t) event->xbutton.y;
10481
        state|=UpdateConfigurationState | ExitState;
10482
        break;
10483
      }
10484
      case MotionNotify:
10485
      {
10486
        pan_info.x=(ssize_t) event->xmotion.x;
10487
        pan_info.y=(ssize_t) event->xmotion.y;
10488
        state|=UpdateConfigurationState;
10489
      }
10490
      default:
10491
        break;
10492
    }
10493
    if ((state & UpdateConfigurationState) != 0)
10494
      {
10495
        /*
10496
          Check boundary conditions.
10497
        */
10498
        if (pan_info.x < (ssize_t) (pan_info.width/2))
10499
          pan_info.x=0;
10500
        else
10501
          pan_info.x=(ssize_t) (x_factor*(pan_info.x-((int) pan_info.width/2)));
10502
        if (pan_info.x < 0)
10503
          pan_info.x=0;
10504
        else
10505
          if ((int) (pan_info.x+windows->image.width) >
10506
              windows->image.ximage->width)
10507
            pan_info.x=(ssize_t) (windows->image.ximage->width-(int) windows->image.width);
10508
        if (pan_info.y < (ssize_t) (pan_info.height/2))
10509
          pan_info.y=0;
10510
        else
10511
          pan_info.y=(ssize_t) (y_factor*(pan_info.y-((int) pan_info.height/2)));
10512
        if (pan_info.y < 0)
10513
          pan_info.y=0;
10514
        else
10515
          if ((int) (pan_info.y+windows->image.height) >
10516
              windows->image.ximage->height)
10517
            pan_info.y=(ssize_t) (windows->image.ximage->height-(int)
10518
              windows->image.height);
10519
        if ((windows->image.x != (int) pan_info.x) ||
10520
            (windows->image.y != (int) pan_info.y))
10521
          {
10522
            /*
10523
              Display image pan offset.
10524
            */
10525
            windows->image.x=(int) pan_info.x;
10526
            windows->image.y=(int) pan_info.y;
10527
            (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10528
              windows->image.width,windows->image.height,windows->image.x,
10529
              windows->image.y);
10530
            XInfoWidget(display,windows,text);
10531
            /*
10532
              Refresh Image window.
10533
            */
10534
            XDrawPanRectangle(display,windows);
10535
            XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10536
          }
10537
        state&=(unsigned int) (~UpdateConfigurationState);
10538
      }
10539
    /*
10540
      Wait for next event.
10541
    */
10542
    if ((state & ExitState) == 0)
10543
      XScreenEvent(display,windows,event,exception);
10544
  } while ((state & ExitState) == 0);
10545
  /*
10546
    Restore cursor.
10547
  */
10548
  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10549
  (void) XFreeCursor(display,cursor);
10550
  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10551
}
10552

10553
/*
10554
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10555
%                                                                             %
10556
%                                                                             %
10557
%                                                                             %
10558
+   X P a s t e I m a g e                                                     %
10559
%                                                                             %
10560
%                                                                             %
10561
%                                                                             %
10562
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10563
%
10564
%  XPasteImage() pastes an image previously saved with XCropImage in the X
10565
%  window image at a location the user chooses with the pointer.
10566
%
10567
%  The format of the XPasteImage method is:
10568
%
10569
%      MagickBooleanType XPasteImage(Display *display,
10570
%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10571
%        ExceptionInfo *exception)
10572
%
10573
%  A description of each parameter follows:
10574
%
10575
%    o display: Specifies a connection to an X server;  returned from
10576
%      XOpenDisplay.
10577
%
10578
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10579
%
10580
%    o windows: Specifies a pointer to a XWindows structure.
10581
%
10582
%    o image: the image; returned from ReadImage.
10583
%
10584
%    o exception: return any errors or warnings in this structure.
10585
%
10586
*/
10587
static MagickBooleanType XPasteImage(Display *display,
10588
  XResourceInfo *resource_info,XWindows *windows,Image *image,
10589
  ExceptionInfo *exception)
10590
{
10591
  const char
10592
    *const PasteMenu[] =
10593
    {
10594
      "Operator",
10595
      "Help",
10596
      "Dismiss",
10597
      (char *) NULL
10598
    };
10599
10600
  static const ModeType
10601
    PasteCommands[] =
10602
    {
10603
      PasteOperatorsCommand,
10604
      PasteHelpCommand,
10605
      PasteDismissCommand
10606
    };
10607
10608
  static CompositeOperator
10609
    compose = CopyCompositeOp;
10610
10611
  char
10612
    text[MagickPathExtent];
10613
10614
  Cursor
10615
    cursor;
10616
10617
  Image
10618
    *paste_image;
10619
10620
  int
10621
    entry,
10622
    id,
10623
    x,
10624
    y;
10625
10626
  double
10627
    scale_factor;
10628
10629
  RectangleInfo
10630
    highlight_info,
10631
    paste_info;
10632
10633
  unsigned int
10634
    height,
10635
    width;
10636
10637
  size_t
10638
    state;
10639
10640
  XEvent
10641
    event;
10642
10643
  /*
10644
    Copy image.
10645
  */
10646
  if (resource_info->copy_image == (Image *) NULL)
10647
    return(MagickFalse);
10648
  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10649
  if (paste_image == (Image *) NULL)
10650
    return(MagickFalse);
10651
  /*
10652
    Map Command widget.
10653
  */
10654
  (void) CloneString(&windows->command.name,"Paste");
10655
  windows->command.data=1;
10656
  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10657
  (void) XMapRaised(display,windows->command.id);
10658
  XClientMessage(display,windows->image.id,windows->im_protocols,
10659
    windows->im_update_widget,CurrentTime);
10660
  /*
10661
    Track pointer until button 1 is pressed.
10662
  */
10663
  XSetCursorState(display,windows,MagickFalse);
10664
  XQueryPosition(display,windows->image.id,&x,&y);
10665
  (void) XSelectInput(display,windows->image.id,
10666
    windows->image.attributes.event_mask | PointerMotionMask);
10667
  paste_info.x=(ssize_t) windows->image.x+x;
10668
  paste_info.y=(ssize_t) windows->image.y+y;
10669
  paste_info.width=0;
10670
  paste_info.height=0;
10671
  cursor=XCreateFontCursor(display,XC_ul_angle);
10672
  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10673
  state=DefaultState;
10674
  do
10675
  {
10676
    if (windows->info.mapped != MagickFalse)
10677
      {
10678
        /*
10679
          Display pointer position.
10680
        */
10681
        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10682
          (long) paste_info.x,(long) paste_info.y);
10683
        XInfoWidget(display,windows,text);
10684
      }
10685
    highlight_info=paste_info;
10686
    highlight_info.x=paste_info.x-windows->image.x;
10687
    highlight_info.y=paste_info.y-windows->image.y;
10688
    XHighlightRectangle(display,windows->image.id,
10689
      windows->image.highlight_context,&highlight_info);
10690
    /*
10691
      Wait for next event.
10692
    */
10693
    XScreenEvent(display,windows,&event,exception);
10694
    XHighlightRectangle(display,windows->image.id,
10695
      windows->image.highlight_context,&highlight_info);
10696
    if (event.xany.window == windows->command.id)
10697
      {
10698
        /*
10699
          Select a command from the Command widget.
10700
        */
10701
        id=XCommandWidget(display,windows,PasteMenu,&event);
10702
        if (id < 0)
10703
          continue;
10704
        switch (PasteCommands[id])
10705
        {
10706
          case PasteOperatorsCommand:
10707
          {
10708
            char
10709
              command[MagickPathExtent],
10710
              **operators;
10711
10712
            /*
10713
              Select a command from the pop-up menu.
10714
            */
10715
            operators=GetCommandOptions(MagickComposeOptions);
10716
            if (operators == (char **) NULL)
10717
              break;
10718
            entry=XMenuWidget(display,windows,PasteMenu[id],
10719
              (const char **) operators,command);
10720
            if (entry >= 0)
10721
              compose=(CompositeOperator) ParseCommandOption(
10722
                MagickComposeOptions,MagickFalse,operators[entry]);
10723
            operators=DestroyStringList(operators);
10724
            break;
10725
          }
10726
          case PasteHelpCommand:
10727
          {
10728
            XTextViewHelp(display,resource_info,windows,MagickFalse,
10729
              "Help Viewer - Image Composite",ImagePasteHelp);
10730
            break;
10731
          }
10732
          case PasteDismissCommand:
10733
          {
10734
            /*
10735
              Prematurely exit.
10736
            */
10737
            state|=EscapeState;
10738
            state|=ExitState;
10739
            break;
10740
          }
10741
          default:
10742
            break;
10743
        }
10744
        continue;
10745
      }
10746
    switch (event.type)
10747
    {
10748
      case ButtonPress:
10749
      {
10750
        if (resource_info->debug != MagickFalse)
10751
          (void) LogMagickEvent(X11Event,GetMagickModule(),
10752
            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10753
            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10754
        if (event.xbutton.button != Button1)
10755
          break;
10756
        if (event.xbutton.window != windows->image.id)
10757
          break;
10758
        /*
10759
          Paste rectangle is relative to image configuration.
10760
        */
10761
        width=(unsigned int) image->columns;
10762
        height=(unsigned int) image->rows;
10763
        x=0;
10764
        y=0;
10765
        if (windows->image.crop_geometry != (char *) NULL)
10766
          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10767
            &width,&height);
10768
        scale_factor=(double) windows->image.ximage->width/width;
10769
        paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10770
        scale_factor=(double) windows->image.ximage->height/height;
10771
        paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10772
        (void) XCheckDefineCursor(display,windows->image.id,cursor);
10773
        paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10774
        paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10775
        break;
10776
      }
10777
      case ButtonRelease:
10778
      {
10779
        if (resource_info->debug != MagickFalse)
10780
          (void) LogMagickEvent(X11Event,GetMagickModule(),
10781
            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10782
            event.xbutton.button,event.xbutton.x,event.xbutton.y);
10783
        if (event.xbutton.button != Button1)
10784
          break;
10785
        if (event.xbutton.window != windows->image.id)
10786
          break;
10787
        if ((paste_info.width != 0) && (paste_info.height != 0))
10788
          {
10789
            /*
10790
              User has selected the location of the paste image.
10791
            */
10792
            paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10793
            paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10794
            state|=ExitState;
10795
          }
10796
        break;
10797
      }
10798
      case Expose:
10799
        break;
10800
      case KeyPress:
10801
      {
10802
        char
10803
          command[MagickPathExtent];
10804
10805
        KeySym
10806
          key_symbol;
10807
10808
        int
10809
          length;
10810
10811
        if (event.xkey.window != windows->image.id)
10812
          break;
10813
        /*
10814
          Respond to a user key press.
10815
        */
10816
        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10817
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10818
        *(command+length)='\0';
10819
        if (resource_info->debug != MagickFalse)
10820
          (void) LogMagickEvent(X11Event,GetMagickModule(),
10821
            "Key press: 0x%lx (%s)",(long) key_symbol,command);
10822
        switch ((int) key_symbol)
10823
        {
10824
          case XK_Escape:
10825
          case XK_F20:
10826
          {
10827
            /*
10828
              Prematurely exit.
10829
            */
10830
            paste_image=DestroyImage(paste_image);
10831
            state|=EscapeState;
10832
            state|=ExitState;
10833
            break;
10834
          }
10835
          case XK_F1:
10836
          case XK_Help:
10837
          {
10838
            (void) XSetFunction(display,windows->image.highlight_context,
10839
              GXcopy);
10840
            XTextViewHelp(display,resource_info,windows,MagickFalse,
10841
              "Help Viewer - Image Composite",ImagePasteHelp);
10842
            (void) XSetFunction(display,windows->image.highlight_context,
10843
              GXinvert);
10844
            break;
10845
          }
10846
          default:
10847
          {
10848
            (void) XBell(display,0);
10849
            break;
10850
          }
10851
        }
10852
        break;
10853
      }
10854
      case MotionNotify:
10855
      {
10856
        /*
10857
          Map and unmap Info widget as text cursor crosses its boundaries.
10858
        */
10859
        x=event.xmotion.x;
10860
        y=event.xmotion.y;
10861
        if (windows->info.mapped != MagickFalse)
10862
          {
10863
            if ((x < (windows->info.x+(int) windows->info.width)) &&
10864
                (y < (windows->info.y+(int) windows->info.height)))
10865
              (void) XWithdrawWindow(display,windows->info.id,
10866
                windows->info.screen);
10867
          }
10868
        else
10869
          if ((x > (windows->info.x+(int) windows->info.width)) ||
10870
              (y > (windows->info.y+(int) windows->info.height)))
10871
            (void) XMapWindow(display,windows->info.id);
10872
        paste_info.x=(ssize_t) windows->image.x+x;
10873
        paste_info.y=(ssize_t) windows->image.y+y;
10874
        break;
10875
      }
10876
      default:
10877
      {
10878
        if (resource_info->debug != MagickFalse)
10879
          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10880
            event.type);
10881
        break;
10882
      }
10883
    }
10884
  } while ((state & ExitState) == 0);
10885
  (void) XSelectInput(display,windows->image.id,
10886
    windows->image.attributes.event_mask);
10887
  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10888
  XSetCursorState(display,windows,MagickFalse);
10889
  (void) XFreeCursor(display,cursor);
10890
  if ((state & EscapeState) != 0)
10891
    return(MagickTrue);
10892
  /*
10893
    Image pasting is relative to image configuration.
10894
  */
10895
  XSetCursorState(display,windows,MagickTrue);
10896
  XCheckRefreshWindows(display,windows);
10897
  width=(unsigned int) image->columns;
10898
  height=(unsigned int) image->rows;
10899
  x=0;
10900
  y=0;
10901
  if (windows->image.crop_geometry != (char *) NULL)
10902
    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10903
  scale_factor=(double) width/windows->image.ximage->width;
10904
  paste_info.x+=x;
10905
  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10906
  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10907
  scale_factor=(double) height/windows->image.ximage->height;
10908
  paste_info.y+=y;
10909
  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10910
  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10911
  /*
10912
    Paste image with X Image window.
10913
  */
10914
  (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10915
    paste_info.y,exception);
10916
  paste_image=DestroyImage(paste_image);
10917
  XSetCursorState(display,windows,MagickFalse);
10918
  /*
10919
    Update image colormap.
10920
  */
10921
  XConfigureImageColormap(display,resource_info,windows,image,exception);
10922
  (void) XConfigureImage(display,resource_info,windows,image,exception);
10923
  return(MagickTrue);
10924
}
10925

10926
/*
10927
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10928
%                                                                             %
10929
%                                                                             %
10930
%                                                                             %
10931
+   X P r i n t I m a g e                                                     %
10932
%                                                                             %
10933
%                                                                             %
10934
%                                                                             %
10935
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10936
%
10937
%  XPrintImage() prints an image to a Postscript printer.
10938
%
10939
%  The format of the XPrintImage method is:
10940
%
10941
%      MagickBooleanType XPrintImage(Display *display,
10942
%        XResourceInfo *resource_info,XWindows *windows,Image *image,
10943
%        ExceptionInfo *exception)
10944
%
10945
%  A description of each parameter follows:
10946
%
10947
%    o display: Specifies a connection to an X server; returned from
10948
%      XOpenDisplay.
10949
%
10950
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10951
%
10952
%    o windows: Specifies a pointer to a XWindows structure.
10953
%
10954
%    o image: the image.
10955
%
10956
%    o exception: return any errors or warnings in this structure.
10957
%
10958
*/
10959
static MagickBooleanType XPrintImage(Display *display,
10960
  XResourceInfo *resource_info,XWindows *windows,Image *image,
10961
  ExceptionInfo *exception)
10962
{
10963
  char
10964
    filename[MagickPathExtent],
10965
    geometry[MagickPathExtent];
10966
10967
  const char
10968
    *const PageSizes[] =
10969
    {
10970
      "Letter",
10971
      "Tabloid",
10972
      "Ledger",
10973
      "Legal",
10974
      "Statement",
10975
      "Executive",
10976
      "A3",
10977
      "A4",
10978
      "A5",
10979
      "B4",
10980
      "B5",
10981
      "Folio",
10982
      "Quarto",
10983
      "10x14",
10984
      (char *) NULL
10985
    };
10986
10987
  Image
10988
    *print_image;
10989
10990
  ImageInfo
10991
    *image_info;
10992
10993
  MagickStatusType
10994
    status;
10995
10996
  /*
10997
    Request Postscript page geometry from user.
10998
  */
10999
  image_info=CloneImageInfo(resource_info->image_info);
11000
  (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
11001
  if (image_info->page != (char *) NULL)
11002
    (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
11003
  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11004
    "Select Postscript Page Geometry:",geometry);
11005
  if (*geometry == '\0')
11006
    return(MagickTrue);
11007
  image_info->page=GetPageGeometry(geometry);
11008
  /*
11009
    Apply image transforms.
11010
  */
11011
  XSetCursorState(display,windows,MagickTrue);
11012
  XCheckRefreshWindows(display,windows);
11013
  print_image=CloneImage(image,0,0,MagickTrue,exception);
11014
  if (print_image == (Image *) NULL)
11015
    return(MagickFalse);
11016
  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11017
    windows->image.ximage->width,windows->image.ximage->height);
11018
  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11019
    exception);
11020
  /*
11021
    Print image.
11022
  */
11023
  (void) AcquireUniqueFilename(filename);
11024
  (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11025
    filename);
11026
  status=WriteImage(image_info,print_image,exception);
11027
  (void) RelinquishUniqueFileResource(filename);
11028
  print_image=DestroyImage(print_image);
11029
  image_info=DestroyImageInfo(image_info);
11030
  XSetCursorState(display,windows,MagickFalse);
11031
  return(status != 0 ? MagickTrue : MagickFalse);
11032
}
11033

11034
/*
11035
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11036
%                                                                             %
11037
%                                                                             %
11038
%                                                                             %
11039
+   X R O I I m a g e                                                         %
11040
%                                                                             %
11041
%                                                                             %
11042
%                                                                             %
11043
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11044
%
11045
%  XROIImage() applies an image processing technique to a region of interest.
11046
%
11047
%  The format of the XROIImage method is:
11048
%
11049
%      MagickBooleanType XROIImage(Display *display,
11050
%        XResourceInfo *resource_info,XWindows *windows,Image **image,
11051
%        ExceptionInfo *exception)
11052
%
11053
%  A description of each parameter follows:
11054
%
11055
%    o display: Specifies a connection to an X server; returned from
11056
%      XOpenDisplay.
11057
%
11058
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11059
%
11060
%    o windows: Specifies a pointer to a XWindows structure.
11061
%
11062
%    o image: the image; returned from ReadImage.
11063
%
11064
%    o exception: return any errors or warnings in this structure.
11065
%
11066
*/
11067
static MagickBooleanType XROIImage(Display *display,
11068
  XResourceInfo *resource_info,XWindows *windows,Image **image,
11069
  ExceptionInfo *exception)
11070
{
11071
#define ApplyMenus  7
11072
11073
  const char
11074
    *const ROIMenu[] =
11075
    {
11076
      "Help",
11077
      "Dismiss",
11078
      (char *) NULL
11079
    },
11080
    *const ApplyMenu[] =
11081
    {
11082
      "File",
11083
      "Edit",
11084
      "Transform",
11085
      "Enhance",
11086
      "Effects",
11087
      "F/X",
11088
      "Miscellany",
11089
      "Help",
11090
      "Dismiss",
11091
      (char *) NULL
11092
    },
11093
    *const FileMenu[] =
11094
    {
11095
      "Save...",
11096
      "Print...",
11097
      (char *) NULL
11098
    },
11099
    *const EditMenu[] =
11100
    {
11101
      "Undo",
11102
      "Redo",
11103
      (char *) NULL
11104
    },
11105
    *const TransformMenu[] =
11106
    {
11107
      "Flop",
11108
      "Flip",
11109
      "Rotate Right",
11110
      "Rotate Left",
11111
      (char *) NULL
11112
    },
11113
    *const EnhanceMenu[] =
11114
    {
11115
      "Hue...",
11116
      "Saturation...",
11117
      "Brightness...",
11118
      "Gamma...",
11119
      "Spiff",
11120
      "Dull",
11121
      "Contrast Stretch...",
11122
      "Sigmoidal Contrast...",
11123
      "Normalize",
11124
      "Equalize",
11125
      "Negate",
11126
      "Grayscale",
11127
      "Map...",
11128
      "Quantize...",
11129
      (char *) NULL
11130
    },
11131
    *const EffectsMenu[] =
11132
    {
11133
      "Despeckle",
11134
      "Emboss",
11135
      "Reduce Noise",
11136
      "Add Noise",
11137
      "Sharpen...",
11138
      "Blur...",
11139
      "Threshold...",
11140
      "Edge Detect...",
11141
      "Spread...",
11142
      "Shade...",
11143
      "Raise...",
11144
      "Segment...",
11145
      (char *) NULL
11146
    },
11147
    *const FXMenu[] =
11148
    {
11149
      "Solarize...",
11150
      "Sepia Tone...",
11151
      "Swirl...",
11152
      "Implode...",
11153
      "Vignette...",
11154
      "Wave...",
11155
      "Oil Paint...",
11156
      "Charcoal Draw...",
11157
      (char *) NULL
11158
    },
11159
    *const MiscellanyMenu[] =
11160
    {
11161
      "Image Info",
11162
      "Zoom Image",
11163
      "Show Preview...",
11164
      "Show Histogram",
11165
      "Show Matte",
11166
      (char *) NULL
11167
    };
11168
11169
  const char
11170
    *const *Menus[ApplyMenus] =
11171
    {
11172
      FileMenu,
11173
      EditMenu,
11174
      TransformMenu,
11175
      EnhanceMenu,
11176
      EffectsMenu,
11177
      FXMenu,
11178
      MiscellanyMenu
11179
    };
11180
11181
  static const DisplayCommand
11182
    ApplyCommands[] =
11183
    {
11184
      NullCommand,
11185
      NullCommand,
11186
      NullCommand,
11187
      NullCommand,
11188
      NullCommand,
11189
      NullCommand,
11190
      NullCommand,
11191
      HelpCommand,
11192
      QuitCommand
11193
    },
11194
    FileCommands[] =
11195
    {
11196
      SaveCommand,
11197
      PrintCommand
11198
    },
11199
    EditCommands[] =
11200
    {
11201
      UndoCommand,
11202
      RedoCommand
11203
    },
11204
    TransformCommands[] =
11205
    {
11206
      FlopCommand,
11207
      FlipCommand,
11208
      RotateRightCommand,
11209
      RotateLeftCommand
11210
    },
11211
    EnhanceCommands[] =
11212
    {
11213
      HueCommand,
11214
      SaturationCommand,
11215
      BrightnessCommand,
11216
      GammaCommand,
11217
      SpiffCommand,
11218
      DullCommand,
11219
      ContrastStretchCommand,
11220
      SigmoidalContrastCommand,
11221
      NormalizeCommand,
11222
      EqualizeCommand,
11223
      NegateCommand,
11224
      GrayscaleCommand,
11225
      MapCommand,
11226
      QuantizeCommand
11227
    },
11228
    EffectsCommands[] =
11229
    {
11230
      DespeckleCommand,
11231
      EmbossCommand,
11232
      ReduceNoiseCommand,
11233
      AddNoiseCommand,
11234
      SharpenCommand,
11235
      BlurCommand,
11236
      ThresholdCommand,
11237
      EdgeDetectCommand,
11238
      SpreadCommand,
11239
      ShadeCommand,
11240
      RaiseCommand,
11241
      SegmentCommand
11242
    },
11243
    FXCommands[] =
11244
    {
11245
      SolarizeCommand,
11246
      SepiaToneCommand,
11247
      SwirlCommand,
11248
      ImplodeCommand,
11249
      VignetteCommand,
11250
      WaveCommand,
11251
      OilPaintCommand,
11252
      CharcoalDrawCommand
11253
    },
11254
    MiscellanyCommands[] =
11255
    {
11256
      InfoCommand,
11257
      ZoomCommand,
11258
      ShowPreviewCommand,
11259
      ShowHistogramCommand,
11260
      ShowMatteCommand
11261
    },
11262
    ROICommands[] =
11263
    {
11264
      ROIHelpCommand,
11265
      ROIDismissCommand
11266
    };
11267
11268
  static const DisplayCommand
11269
    *Commands[ApplyMenus] =
11270
    {
11271
      FileCommands,
11272
      EditCommands,
11273
      TransformCommands,
11274
      EnhanceCommands,
11275
      EffectsCommands,
11276
      FXCommands,
11277
      MiscellanyCommands
11278
    };
11279
11280
  char
11281
    command[MagickPathExtent],
11282
    text[MagickPathExtent];
11283
11284
  DisplayCommand
11285
    display_command;
11286
11287
  Cursor
11288
    cursor;
11289
11290
  Image
11291
    *roi_image;
11292
11293
  int
11294
    entry,
11295
    id,
11296
    x,
11297
    y;
11298
11299
  double
11300
    scale_factor;
11301
11302
  MagickProgressMonitor
11303
    progress_monitor;
11304
11305
  RectangleInfo
11306
    crop_info,
11307
    highlight_info,
11308
    roi_info;
11309
11310
  unsigned int
11311
    height,
11312
    width;
11313
11314
  size_t
11315
    state;
11316
11317
  XEvent
11318
    event;
11319
11320
  /*
11321
    Map Command widget.
11322
  */
11323
  (void) CloneString(&windows->command.name,"ROI");
11324
  windows->command.data=0;
11325
  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11326
  (void) XMapRaised(display,windows->command.id);
11327
  XClientMessage(display,windows->image.id,windows->im_protocols,
11328
    windows->im_update_widget,CurrentTime);
11329
  /*
11330
    Track pointer until button 1 is pressed.
11331
  */
11332
  XQueryPosition(display,windows->image.id,&x,&y);
11333
  (void) XSelectInput(display,windows->image.id,
11334
    windows->image.attributes.event_mask | PointerMotionMask);
11335
  roi_info.x=(ssize_t) windows->image.x+x;
11336
  roi_info.y=(ssize_t) windows->image.y+y;
11337
  roi_info.width=0;
11338
  roi_info.height=0;
11339
  cursor=XCreateFontCursor(display,XC_fleur);
11340
  state=DefaultState;
11341
  do
11342
  {
11343
    if (windows->info.mapped != MagickFalse)
11344
      {
11345
        /*
11346
          Display pointer position.
11347
        */
11348
        (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11349
          (long) roi_info.x,(long) roi_info.y);
11350
        XInfoWidget(display,windows,text);
11351
      }
11352
    /*
11353
      Wait for next event.
11354
    */
11355
    XScreenEvent(display,windows,&event,exception);
11356
    if (event.xany.window == windows->command.id)
11357
      {
11358
        /*
11359
          Select a command from the Command widget.
11360
        */
11361
        id=XCommandWidget(display,windows,ROIMenu,&event);
11362
        if (id < 0)
11363
          continue;
11364
        switch (ROICommands[id])
11365
        {
11366
          case ROIHelpCommand:
11367
          {
11368
            XTextViewHelp(display,resource_info,windows,MagickFalse,
11369
              "Help Viewer - Region of Interest",ImageROIHelp);
11370
            break;
11371
          }
11372
          case ROIDismissCommand:
11373
          {
11374
            /*
11375
              Prematurely exit.
11376
            */
11377
            state|=EscapeState;
11378
            state|=ExitState;
11379
            break;
11380
          }
11381
          default:
11382
            break;
11383
        }
11384
        continue;
11385
      }
11386
    switch (event.type)
11387
    {
11388
      case ButtonPress:
11389
      {
11390
        if (event.xbutton.button != Button1)
11391
          break;
11392
        if (event.xbutton.window != windows->image.id)
11393
          break;
11394
        /*
11395
          Note first corner of region of interest rectangle-- exit loop.
11396
        */
11397
        (void) XCheckDefineCursor(display,windows->image.id,cursor);
11398
        roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11399
        roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11400
        state|=ExitState;
11401
        break;
11402
      }
11403
      case ButtonRelease:
11404
        break;
11405
      case Expose:
11406
        break;
11407
      case KeyPress:
11408
      {
11409
        KeySym
11410
          key_symbol;
11411
11412
        if (event.xkey.window != windows->image.id)
11413
          break;
11414
        /*
11415
          Respond to a user key press.
11416
        */
11417
        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11418
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11419
        switch ((int) key_symbol)
11420
        {
11421
          case XK_Escape:
11422
          case XK_F20:
11423
          {
11424
            /*
11425
              Prematurely exit.
11426
            */
11427
            state|=EscapeState;
11428
            state|=ExitState;
11429
            break;
11430
          }
11431
          case XK_F1:
11432
          case XK_Help:
11433
          {
11434
            XTextViewHelp(display,resource_info,windows,MagickFalse,
11435
              "Help Viewer - Region of Interest",ImageROIHelp);
11436
            break;
11437
          }
11438
          default:
11439
          {
11440
            (void) XBell(display,0);
11441
            break;
11442
          }
11443
        }
11444
        break;
11445
      }
11446
      case MotionNotify:
11447
      {
11448
        /*
11449
          Map and unmap Info widget as text cursor crosses its boundaries.
11450
        */
11451
        x=event.xmotion.x;
11452
        y=event.xmotion.y;
11453
        if (windows->info.mapped != MagickFalse)
11454
          {
11455
            if ((x < (windows->info.x+(int) windows->info.width)) &&
11456
                (y < (windows->info.y+(int) windows->info.height)))
11457
              (void) XWithdrawWindow(display,windows->info.id,
11458
                windows->info.screen);
11459
          }
11460
        else
11461
          if ((x > (windows->info.x+(int) windows->info.width)) ||
11462
              (y > (windows->info.y+(int) windows->info.height)))
11463
            (void) XMapWindow(display,windows->info.id);
11464
        roi_info.x=(ssize_t) windows->image.x+x;
11465
        roi_info.y=(ssize_t) windows->image.y+y;
11466
        break;
11467
      }
11468
      default:
11469
        break;
11470
    }
11471
  } while ((state & ExitState) == 0);
11472
  (void) XSelectInput(display,windows->image.id,
11473
    windows->image.attributes.event_mask);
11474
  if ((state & EscapeState) != 0)
11475
    {
11476
      /*
11477
        User want to exit without region of interest.
11478
      */
11479
      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11480
      (void) XFreeCursor(display,cursor);
11481
      return(MagickTrue);
11482
    }
11483
  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11484
  do
11485
  {
11486
    /*
11487
      Size rectangle as pointer moves until the mouse button is released.
11488
    */
11489
    x=(int) roi_info.x;
11490
    y=(int) roi_info.y;
11491
    roi_info.width=0;
11492
    roi_info.height=0;
11493
    state=DefaultState;
11494
    do
11495
    {
11496
      highlight_info=roi_info;
11497
      highlight_info.x=roi_info.x-windows->image.x;
11498
      highlight_info.y=roi_info.y-windows->image.y;
11499
      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11500
        {
11501
          /*
11502
            Display info and draw region of interest rectangle.
11503
          */
11504
          if (windows->info.mapped == MagickFalse)
11505
            (void) XMapWindow(display,windows->info.id);
11506
          (void) FormatLocaleString(text,MagickPathExtent,
11507
            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11508
            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11509
          XInfoWidget(display,windows,text);
11510
          XHighlightRectangle(display,windows->image.id,
11511
            windows->image.highlight_context,&highlight_info);
11512
        }
11513
      else
11514
        if (windows->info.mapped != MagickFalse)
11515
          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11516
      /*
11517
        Wait for next event.
11518
      */
11519
      XScreenEvent(display,windows,&event,exception);
11520
      if ((highlight_info.width > 3) && (highlight_info.height > 3))
11521
        XHighlightRectangle(display,windows->image.id,
11522
          windows->image.highlight_context,&highlight_info);
11523
      switch (event.type)
11524
      {
11525
        case ButtonPress:
11526
        {
11527
          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11528
          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11529
          break;
11530
        }
11531
        case ButtonRelease:
11532
        {
11533
          /*
11534
            User has committed to region of interest rectangle.
11535
          */
11536
          roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11537
          roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11538
          XSetCursorState(display,windows,MagickFalse);
11539
          state|=ExitState;
11540
          if (LocaleCompare(windows->command.name,"Apply") == 0)
11541
            break;
11542
          (void) CloneString(&windows->command.name,"Apply");
11543
          windows->command.data=ApplyMenus;
11544
          (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11545
          break;
11546
        }
11547
        case Expose:
11548
          break;
11549
        case MotionNotify:
11550
        {
11551
          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11552
          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11553
        }
11554
        default:
11555
          break;
11556
      }
11557
      if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11558
          ((state & ExitState) != 0))
11559
        {
11560
          /*
11561
            Check boundary conditions.
11562
          */
11563
          if (roi_info.x < 0)
11564
            roi_info.x=0;
11565
          else
11566
            if (roi_info.x > (ssize_t) windows->image.ximage->width)
11567
              roi_info.x=(ssize_t) windows->image.ximage->width;
11568
          if ((int) roi_info.x < x)
11569
            roi_info.width=(unsigned int) (x-roi_info.x);
11570
          else
11571
            {
11572
              roi_info.width=(unsigned int) (roi_info.x-x);
11573
              roi_info.x=(ssize_t) x;
11574
            }
11575
          if (roi_info.y < 0)
11576
            roi_info.y=0;
11577
          else
11578
            if (roi_info.y > (ssize_t) windows->image.ximage->height)
11579
              roi_info.y=(ssize_t) windows->image.ximage->height;
11580
          if ((int) roi_info.y < y)
11581
            roi_info.height=(unsigned int) (y-roi_info.y);
11582
          else
11583
            {
11584
              roi_info.height=(unsigned int) (roi_info.y-y);
11585
              roi_info.y=(ssize_t) y;
11586
            }
11587
        }
11588
    } while ((state & ExitState) == 0);
11589
    /*
11590
      Wait for user to grab a corner of the rectangle or press return.
11591
    */
11592
    state=DefaultState;
11593
    display_command=NullCommand;
11594
    crop_info.x=0;
11595
    crop_info.y=0;
11596
    (void) XMapWindow(display,windows->info.id);
11597
    do
11598
    {
11599
      if (windows->info.mapped != MagickFalse)
11600
        {
11601
          /*
11602
            Display pointer position.
11603
          */
11604
          (void) FormatLocaleString(text,MagickPathExtent,
11605
            " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11606
            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11607
          XInfoWidget(display,windows,text);
11608
        }
11609
      highlight_info=roi_info;
11610
      highlight_info.x=roi_info.x-windows->image.x;
11611
      highlight_info.y=roi_info.y-windows->image.y;
11612
      if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11613
        {
11614
          state|=EscapeState;
11615
          state|=ExitState;
11616
          break;
11617
        }
11618
      if ((state & UpdateRegionState) != 0)
11619
        {
11620
          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11621
          switch (display_command)
11622
          {
11623
            case UndoCommand:
11624
            case RedoCommand:
11625
            {
11626
              (void) XMagickCommand(display,resource_info,windows,
11627
                display_command,image,exception);
11628
              break;
11629
            }
11630
            default:
11631
            {
11632
              /*
11633
                Region of interest is relative to image configuration.
11634
              */
11635
              progress_monitor=SetImageProgressMonitor(*image,
11636
                (MagickProgressMonitor) NULL,(*image)->client_data);
11637
              crop_info=roi_info;
11638
              width=(unsigned int) (*image)->columns;
11639
              height=(unsigned int) (*image)->rows;
11640
              x=0;
11641
              y=0;
11642
              if (windows->image.crop_geometry != (char *) NULL)
11643
                (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11644
                  &width,&height);
11645
              scale_factor=(double) width/windows->image.ximage->width;
11646
              crop_info.x+=x;
11647
              crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11648
              crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11649
              scale_factor=(double)
11650
                height/windows->image.ximage->height;
11651
              crop_info.y+=y;
11652
              crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11653
              crop_info.height=(unsigned int)
11654
                (scale_factor*crop_info.height+0.5);
11655
              roi_image=CropImage(*image,&crop_info,exception);
11656
              (void) SetImageProgressMonitor(*image,progress_monitor,
11657
                (*image)->client_data);
11658
              if (roi_image == (Image *) NULL)
11659
                continue;
11660
              /*
11661
                Apply image processing technique to the region of interest.
11662
              */
11663
              windows->image.orphan=MagickTrue;
11664
              (void) XMagickCommand(display,resource_info,windows,
11665
                display_command,&roi_image,exception);
11666
              progress_monitor=SetImageProgressMonitor(*image,
11667
                (MagickProgressMonitor) NULL,(*image)->client_data);
11668
              (void) XMagickCommand(display,resource_info,windows,
11669
                SaveToUndoBufferCommand,image,exception);
11670
              windows->image.orphan=MagickFalse;
11671
              (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11672
                MagickTrue,crop_info.x,crop_info.y,exception);
11673
              roi_image=DestroyImage(roi_image);
11674
              (void) SetImageProgressMonitor(*image,progress_monitor,
11675
                (*image)->client_data);
11676
              break;
11677
            }
11678
          }
11679
          if (display_command != InfoCommand)
11680
            {
11681
              XConfigureImageColormap(display,resource_info,windows,*image,
11682
                exception);
11683
              (void) XConfigureImage(display,resource_info,windows,*image,
11684
                exception);
11685
            }
11686
          XCheckRefreshWindows(display,windows);
11687
          XInfoWidget(display,windows,text);
11688
          (void) XSetFunction(display,windows->image.highlight_context,
11689
            GXinvert);
11690
          state&=(unsigned int) (~UpdateRegionState);
11691
        }
11692
      XHighlightRectangle(display,windows->image.id,
11693
        windows->image.highlight_context,&highlight_info);
11694
      XScreenEvent(display,windows,&event,exception);
11695
      if (event.xany.window == windows->command.id)
11696
        {
11697
          /*
11698
            Select a command from the Command widget.
11699
          */
11700
          (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11701
          display_command=NullCommand;
11702
          id=XCommandWidget(display,windows,ApplyMenu,&event);
11703
          if (id >= 0)
11704
            {
11705
              (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11706
              display_command=ApplyCommands[id];
11707
              if (id < ApplyMenus)
11708
                {
11709
                  /*
11710
                    Select a command from a pop-up menu.
11711
                  */
11712
                  entry=XMenuWidget(display,windows,ApplyMenu[id],
11713
                    (const char **) Menus[id],command);
11714
                  if (entry >= 0)
11715
                    {
11716
                      (void) CopyMagickString(command,Menus[id][entry],
11717
                        MagickPathExtent);
11718
                      display_command=Commands[id][entry];
11719
                    }
11720
                }
11721
            }
11722
          (void) XSetFunction(display,windows->image.highlight_context,
11723
            GXinvert);
11724
          XHighlightRectangle(display,windows->image.id,
11725
            windows->image.highlight_context,&highlight_info);
11726
          if (display_command == HelpCommand)
11727
            {
11728
              (void) XSetFunction(display,windows->image.highlight_context,
11729
                GXcopy);
11730
              XTextViewHelp(display,resource_info,windows,MagickFalse,
11731
                "Help Viewer - Region of Interest",ImageROIHelp);
11732
              (void) XSetFunction(display,windows->image.highlight_context,
11733
                GXinvert);
11734
              continue;
11735
            }
11736
          if (display_command == QuitCommand)
11737
            {
11738
              /*
11739
                exit.
11740
              */
11741
              state|=EscapeState;
11742
              state|=ExitState;
11743
              continue;
11744
            }
11745
          if (display_command != NullCommand)
11746
            state|=UpdateRegionState;
11747
          continue;
11748
        }
11749
      XHighlightRectangle(display,windows->image.id,
11750
        windows->image.highlight_context,&highlight_info);
11751
      switch (event.type)
11752
      {
11753
        case ButtonPress:
11754
        {
11755
          x=windows->image.x;
11756
          y=windows->image.y;
11757
          if (event.xbutton.button != Button1)
11758
            break;
11759
          if (event.xbutton.window != windows->image.id)
11760
            break;
11761
          x=windows->image.x+event.xbutton.x;
11762
          y=windows->image.y+event.xbutton.y;
11763
          if ((x < (int) (roi_info.x+RoiDelta)) &&
11764
              (x > (int) (roi_info.x-RoiDelta)) &&
11765
              (y < (int) (roi_info.y+RoiDelta)) &&
11766
              (y > (int) (roi_info.y-RoiDelta)))
11767
            {
11768
              roi_info.x=roi_info.x+(int) roi_info.width;
11769
              roi_info.y=roi_info.y+(int) roi_info.height;
11770
              state|=UpdateConfigurationState;
11771
              break;
11772
            }
11773
          if ((x < (int) (roi_info.x+RoiDelta)) &&
11774
              (x > (int) (roi_info.x-RoiDelta)) &&
11775
              (y < (int) (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11776
              (y > (int) (roi_info.y+(int) roi_info.height-RoiDelta)))
11777
            {
11778
              roi_info.x=roi_info.x+(int) roi_info.width;
11779
              state|=UpdateConfigurationState;
11780
              break;
11781
            }
11782
          if ((x < (int) (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11783
              (x > (int) (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11784
              (y < (int) (roi_info.y+RoiDelta)) &&
11785
              (y > (int) (roi_info.y-RoiDelta)))
11786
            {
11787
              roi_info.y=roi_info.y+(int) roi_info.height;
11788
              state|=UpdateConfigurationState;
11789
              break;
11790
            }
11791
          if ((x < (int) (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11792
              (x > (int) (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11793
              (y < (int) (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11794
              (y > (int) (roi_info.y+(int) roi_info.height-RoiDelta)))
11795
            {
11796
              state|=UpdateConfigurationState;
11797
              break;
11798
            }
11799
          magick_fallthrough;
11800
        }
11801
        case ButtonRelease:
11802
        {
11803
          if (event.xbutton.window == windows->pan.id)
11804
            if ((highlight_info.x != crop_info.x-windows->image.x) ||
11805
                (highlight_info.y != crop_info.y-windows->image.y))
11806
              XHighlightRectangle(display,windows->image.id,
11807
                windows->image.highlight_context,&highlight_info);
11808
          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11809
            event.xbutton.time);
11810
          break;
11811
        }
11812
        case Expose:
11813
        {
11814
          if (event.xexpose.window == windows->image.id)
11815
            if (event.xexpose.count == 0)
11816
              {
11817
                event.xexpose.x=(int) highlight_info.x;
11818
                event.xexpose.y=(int) highlight_info.y;
11819
                event.xexpose.width=(int) highlight_info.width;
11820
                event.xexpose.height=(int) highlight_info.height;
11821
                XRefreshWindow(display,&windows->image,&event);
11822
              }
11823
          if (event.xexpose.window == windows->info.id)
11824
            if (event.xexpose.count == 0)
11825
              XInfoWidget(display,windows,text);
11826
          break;
11827
        }
11828
        case KeyPress:
11829
        {
11830
          KeySym
11831
            key_symbol;
11832
11833
          if (event.xkey.window != windows->image.id)
11834
            break;
11835
          /*
11836
            Respond to a user key press.
11837
          */
11838
          (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11839
            sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11840
          switch ((int) key_symbol)
11841
          {
11842
            case XK_Shift_L:
11843
            case XK_Shift_R:
11844
              break;
11845
            case XK_Escape:
11846
            case XK_F20:
11847
            {
11848
              state|=EscapeState;
11849
              magick_fallthrough;
11850
            }
11851
            case XK_Return:
11852
            {
11853
              state|=ExitState;
11854
              break;
11855
            }
11856
            case XK_Home:
11857
            case XK_KP_Home:
11858
            {
11859
              roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11860
              roi_info.y=(ssize_t) (windows->image.height/2L-
11861
                roi_info.height/2L);
11862
              break;
11863
            }
11864
            case XK_Left:
11865
            case XK_KP_Left:
11866
            {
11867
              roi_info.x--;
11868
              break;
11869
            }
11870
            case XK_Up:
11871
            case XK_KP_Up:
11872
            case XK_Next:
11873
            {
11874
              roi_info.y--;
11875
              break;
11876
            }
11877
            case XK_Right:
11878
            case XK_KP_Right:
11879
            {
11880
              roi_info.x++;
11881
              break;
11882
            }
11883
            case XK_Prior:
11884
            case XK_Down:
11885
            case XK_KP_Down:
11886
            {
11887
              roi_info.y++;
11888
              break;
11889
            }
11890
            case XK_F1:
11891
            case XK_Help:
11892
            {
11893
              (void) XSetFunction(display,windows->image.highlight_context,
11894
                GXcopy);
11895
              XTextViewHelp(display,resource_info,windows,MagickFalse,
11896
                "Help Viewer - Region of Interest",ImageROIHelp);
11897
              (void) XSetFunction(display,windows->image.highlight_context,
11898
                GXinvert);
11899
              break;
11900
            }
11901
            default:
11902
            {
11903
              display_command=XImageWindowCommand(display,resource_info,windows,
11904
                event.xkey.state,key_symbol,image,exception);
11905
              if (display_command != NullCommand)
11906
                state|=UpdateRegionState;
11907
              break;
11908
            }
11909
          }
11910
          (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11911
            event.xkey.time);
11912
          break;
11913
        }
11914
        case KeyRelease:
11915
          break;
11916
        case MotionNotify:
11917
        {
11918
          if (event.xbutton.window != windows->image.id)
11919
            break;
11920
          /*
11921
            Map and unmap Info widget as text cursor crosses its boundaries.
11922
          */
11923
          x=event.xmotion.x;
11924
          y=event.xmotion.y;
11925
          if (windows->info.mapped != MagickFalse)
11926
            {
11927
              if ((x < (windows->info.x+(int) windows->info.width)) &&
11928
                  (y < (windows->info.y+(int) windows->info.height)))
11929
                (void) XWithdrawWindow(display,windows->info.id,
11930
                  windows->info.screen);
11931
            }
11932
          else
11933
            if ((x > (windows->info.x+(int) windows->info.width)) ||
11934
                (y > (windows->info.y+(int) windows->info.height)))
11935
              (void) XMapWindow(display,windows->info.id);
11936
          roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11937
          roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11938
          break;
11939
        }
11940
        case SelectionRequest:
11941
        {
11942
          XSelectionEvent
11943
            notify;
11944
11945
          XSelectionRequestEvent
11946
            *request;
11947
11948
          /*
11949
            Set primary selection.
11950
          */
11951
          (void) FormatLocaleString(text,MagickPathExtent,
11952
            "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11953
            roi_info.height,(double) roi_info.x,(double) roi_info.y);
11954
          request=(&(event.xselectionrequest));
11955
          (void) XChangeProperty(request->display,request->requestor,
11956
            request->property,request->target,8,PropModeReplace,
11957
            (unsigned char *) text,(int) strlen(text));
11958
          notify.type=SelectionNotify;
11959
          notify.display=request->display;
11960
          notify.requestor=request->requestor;
11961
          notify.selection=request->selection;
11962
          notify.target=request->target;
11963
          notify.time=request->time;
11964
          if (request->property == None)
11965
            notify.property=request->target;
11966
          else
11967
            notify.property=request->property;
11968
          (void) XSendEvent(request->display,request->requestor,False,0,
11969
            (XEvent *) &notify);
11970
        }
11971
        default:
11972
          break;
11973
      }
11974
      if ((state & UpdateConfigurationState) != 0)
11975
        {
11976
          (void) XPutBackEvent(display,&event);
11977
          (void) XCheckDefineCursor(display,windows->image.id,cursor);
11978
          break;
11979
        }
11980
    } while ((state & ExitState) == 0);
11981
  } while ((state & ExitState) == 0);
11982
  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11983
  XSetCursorState(display,windows,MagickFalse);
11984
  if ((state & EscapeState) != 0)
11985
    return(MagickTrue);
11986
  return(MagickTrue);
11987
}
11988

11989
/*
11990
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11991
%                                                                             %
11992
%                                                                             %
11993
%                                                                             %
11994
+   X R o t a t e I m a g e                                                   %
11995
%                                                                             %
11996
%                                                                             %
11997
%                                                                             %
11998
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11999
%
12000
%  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
12001
%  rotation angle is computed from the slope of a line drawn by the user.
12002
%
12003
%  The format of the XRotateImage method is:
12004
%
12005
%      MagickBooleanType XRotateImage(Display *display,
12006
%        XResourceInfo *resource_info,XWindows *windows,double degrees,
12007
%        Image **image,ExceptionInfo *exception)
12008
%
12009
%  A description of each parameter follows:
12010
%
12011
%    o display: Specifies a connection to an X server; returned from
12012
%      XOpenDisplay.
12013
%
12014
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12015
%
12016
%    o windows: Specifies a pointer to a XWindows structure.
12017
%
12018
%    o degrees: Specifies the number of degrees to rotate the image.
12019
%
12020
%    o image: the image.
12021
%
12022
%    o exception: return any errors or warnings in this structure.
12023
%
12024
*/
12025
static MagickBooleanType XRotateImage(Display *display,
12026
  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12027
  ExceptionInfo *exception)
12028
{
12029
  const char
12030
    *const RotateMenu[] =
12031
    {
12032
      "Pixel Color",
12033
      "Direction",
12034
      "Help",
12035
      "Dismiss",
12036
      (char *) NULL
12037
    };
12038
12039
  static ModeType
12040
    direction = HorizontalRotateCommand;
12041
12042
  static const ModeType
12043
    DirectionCommands[] =
12044
    {
12045
      HorizontalRotateCommand,
12046
      VerticalRotateCommand
12047
    },
12048
    RotateCommands[] =
12049
    {
12050
      RotateColorCommand,
12051
      RotateDirectionCommand,
12052
      RotateHelpCommand,
12053
      RotateDismissCommand
12054
    };
12055
12056
  static unsigned int
12057
    pen_id = 0;
12058
12059
  char
12060
    command[MagickPathExtent],
12061
    text[MagickPathExtent];
12062
12063
  Image
12064
    *rotate_image;
12065
12066
  int
12067
    id,
12068
    x,
12069
    y;
12070
12071
  double
12072
    normalized_degrees;
12073
12074
  int
12075
    i;
12076
12077
  unsigned int
12078
    height,
12079
    rotations,
12080
    width;
12081
12082
  if (degrees == 0.0)
12083
    {
12084
      unsigned int
12085
        distance;
12086
12087
      size_t
12088
        state;
12089
12090
      XEvent
12091
        event;
12092
12093
      XSegment
12094
        rotate_info;
12095
12096
      /*
12097
        Map Command widget.
12098
      */
12099
      (void) CloneString(&windows->command.name,"Rotate");
12100
      windows->command.data=2;
12101
      (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12102
      (void) XMapRaised(display,windows->command.id);
12103
      XClientMessage(display,windows->image.id,windows->im_protocols,
12104
        windows->im_update_widget,CurrentTime);
12105
      /*
12106
        Wait for first button press.
12107
      */
12108
      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12109
      XQueryPosition(display,windows->image.id,&x,&y);
12110
      rotate_info.x1=x;
12111
      rotate_info.y1=y;
12112
      rotate_info.x2=x;
12113
      rotate_info.y2=y;
12114
      state=DefaultState;
12115
      do
12116
      {
12117
        XHighlightLine(display,windows->image.id,
12118
          windows->image.highlight_context,&rotate_info);
12119
        /*
12120
          Wait for next event.
12121
        */
12122
        XScreenEvent(display,windows,&event,exception);
12123
        XHighlightLine(display,windows->image.id,
12124
          windows->image.highlight_context,&rotate_info);
12125
        if (event.xany.window == windows->command.id)
12126
          {
12127
            /*
12128
              Select a command from the Command widget.
12129
            */
12130
            id=XCommandWidget(display,windows,RotateMenu,&event);
12131
            if (id < 0)
12132
              continue;
12133
            (void) XSetFunction(display,windows->image.highlight_context,
12134
              GXcopy);
12135
            switch (RotateCommands[id])
12136
            {
12137
              case RotateColorCommand:
12138
              {
12139
                const char
12140
                  *ColorMenu[MaxNumberPens];
12141
12142
                int
12143
                  pen_number;
12144
12145
                XColor
12146
                  color;
12147
12148
                /*
12149
                  Initialize menu selections.
12150
                */
12151
                for (i=0; i < (int) (MaxNumberPens-2); i++)
12152
                  ColorMenu[i]=resource_info->pen_colors[i];
12153
                ColorMenu[MaxNumberPens-2]="Browser...";
12154
                ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12155
                /*
12156
                  Select a pen color from the pop-up menu.
12157
                */
12158
                pen_number=XMenuWidget(display,windows,RotateMenu[id],
12159
                  (const char **) ColorMenu,command);
12160
                if (pen_number < 0)
12161
                  break;
12162
                if (pen_number == (MaxNumberPens-2))
12163
                  {
12164
                    static char
12165
                      color_name[MagickPathExtent] = "gray";
12166
12167
                    /*
12168
                      Select a pen color from a dialog.
12169
                    */
12170
                    resource_info->pen_colors[pen_number]=color_name;
12171
                    XColorBrowserWidget(display,windows,"Select",color_name);
12172
                    if (*color_name == '\0')
12173
                      break;
12174
                  }
12175
                /*
12176
                  Set pen color.
12177
                */
12178
                (void) XParseColor(display,windows->map_info->colormap,
12179
                  resource_info->pen_colors[pen_number],&color);
12180
                XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12181
                  (unsigned int) MaxColors,&color);
12182
                windows->pixel_info->pen_colors[pen_number]=color;
12183
                pen_id=(unsigned int) pen_number;
12184
                break;
12185
              }
12186
              case RotateDirectionCommand:
12187
              {
12188
                const char
12189
                  *Directions[] =
12190
                  {
12191
                    "horizontal",
12192
                    "vertical",
12193
                    (char *) NULL,
12194
                  };
12195
12196
                /*
12197
                  Select a command from the pop-up menu.
12198
                */
12199
                id=XMenuWidget(display,windows,RotateMenu[id],
12200
                  Directions,command);
12201
                if (id >= 0)
12202
                  direction=DirectionCommands[id];
12203
                break;
12204
              }
12205
              case RotateHelpCommand:
12206
              {
12207
                XTextViewHelp(display,resource_info,windows,MagickFalse,
12208
                  "Help Viewer - Image Rotation",ImageRotateHelp);
12209
                break;
12210
              }
12211
              case RotateDismissCommand:
12212
              {
12213
                /*
12214
                  Prematurely exit.
12215
                */
12216
                state|=EscapeState;
12217
                state|=ExitState;
12218
                break;
12219
              }
12220
              default:
12221
                break;
12222
            }
12223
            (void) XSetFunction(display,windows->image.highlight_context,
12224
              GXinvert);
12225
            continue;
12226
          }
12227
        switch (event.type)
12228
        {
12229
          case ButtonPress:
12230
          {
12231
            if (event.xbutton.button != Button1)
12232
              break;
12233
            if (event.xbutton.window != windows->image.id)
12234
              break;
12235
            /*
12236
              exit loop.
12237
            */
12238
            (void) XSetFunction(display,windows->image.highlight_context,
12239
              GXcopy);
12240
            rotate_info.x1=event.xbutton.x;
12241
            rotate_info.y1=event.xbutton.y;
12242
            state|=ExitState;
12243
            break;
12244
          }
12245
          case ButtonRelease:
12246
            break;
12247
          case Expose:
12248
            break;
12249
          case KeyPress:
12250
          {
12251
            char
12252
              command[MagickPathExtent];
12253
12254
            KeySym
12255
              key_symbol;
12256
12257
            if (event.xkey.window != windows->image.id)
12258
              break;
12259
            /*
12260
              Respond to a user key press.
12261
            */
12262
            (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12263
              sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12264
            switch ((int) key_symbol)
12265
            {
12266
              case XK_Escape:
12267
              case XK_F20:
12268
              {
12269
                /*
12270
                  Prematurely exit.
12271
                */
12272
                state|=EscapeState;
12273
                state|=ExitState;
12274
                break;
12275
              }
12276
              case XK_F1:
12277
              case XK_Help:
12278
              {
12279
                (void) XSetFunction(display,windows->image.highlight_context,
12280
                  GXcopy);
12281
                XTextViewHelp(display,resource_info,windows,MagickFalse,
12282
                  "Help Viewer - Image Rotation",ImageRotateHelp);
12283
                (void) XSetFunction(display,windows->image.highlight_context,
12284
                  GXinvert);
12285
                break;
12286
              }
12287
              default:
12288
              {
12289
                (void) XBell(display,0);
12290
                break;
12291
              }
12292
            }
12293
            break;
12294
          }
12295
          case MotionNotify:
12296
          {
12297
            rotate_info.x1=event.xmotion.x;
12298
            rotate_info.y1=event.xmotion.y;
12299
          }
12300
        }
12301
        rotate_info.x2=rotate_info.x1;
12302
        rotate_info.y2=rotate_info.y1;
12303
        if (direction == HorizontalRotateCommand)
12304
          rotate_info.x2+=32;
12305
        else
12306
          rotate_info.y2-=32;
12307
      } while ((state & ExitState) == 0);
12308
      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12309
      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12310
      if ((state & EscapeState) != 0)
12311
        return(MagickTrue);
12312
      /*
12313
        Draw line as pointer moves until the mouse button is released.
12314
      */
12315
      distance=0;
12316
      (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12317
      state=DefaultState;
12318
      do
12319
      {
12320
        if (distance > 9)
12321
          {
12322
            /*
12323
              Display info and draw rotation line.
12324
            */
12325
            if (windows->info.mapped == MagickFalse)
12326
              (void) XMapWindow(display,windows->info.id);
12327
            (void) FormatLocaleString(text,MagickPathExtent," %g",
12328
              direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12329
            XInfoWidget(display,windows,text);
12330
            XHighlightLine(display,windows->image.id,
12331
              windows->image.highlight_context,&rotate_info);
12332
          }
12333
        else
12334
          if (windows->info.mapped != MagickFalse)
12335
            (void) XWithdrawWindow(display,windows->info.id,
12336
              windows->info.screen);
12337
        /*
12338
          Wait for next event.
12339
        */
12340
        XScreenEvent(display,windows,&event,exception);
12341
        if (distance > 9)
12342
          XHighlightLine(display,windows->image.id,
12343
            windows->image.highlight_context,&rotate_info);
12344
        switch (event.type)
12345
        {
12346
          case ButtonPress:
12347
            break;
12348
          case ButtonRelease:
12349
          {
12350
            /*
12351
              User has committed to rotation line.
12352
            */
12353
            rotate_info.x2=event.xbutton.x;
12354
            rotate_info.y2=event.xbutton.y;
12355
            state|=ExitState;
12356
            break;
12357
          }
12358
          case Expose:
12359
            break;
12360
          case MotionNotify:
12361
          {
12362
            rotate_info.x2=event.xmotion.x;
12363
            rotate_info.y2=event.xmotion.y;
12364
          }
12365
          default:
12366
            break;
12367
        }
12368
        /*
12369
          Check boundary conditions.
12370
        */
12371
        if (rotate_info.x2 < 0)
12372
          rotate_info.x2=0;
12373
        else
12374
          if (rotate_info.x2 > (int) windows->image.width)
12375
            rotate_info.x2=(short) windows->image.width;
12376
        if (rotate_info.y2 < 0)
12377
          rotate_info.y2=0;
12378
        else
12379
          if (rotate_info.y2 > (int) windows->image.height)
12380
            rotate_info.y2=(short) windows->image.height;
12381
        /*
12382
          Compute rotation angle from the slope of the line.
12383
        */
12384
        degrees=0.0;
12385
        distance=(unsigned int)
12386
          (((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12387
          ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1)));
12388
        if (distance > 9)
12389
          degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12390
            rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12391
      } while ((state & ExitState) == 0);
12392
      (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12393
      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12394
      if (distance <= 9)
12395
        return(MagickTrue);
12396
    }
12397
  if (direction == VerticalRotateCommand)
12398
    degrees-=90.0;
12399
  if (degrees == 0.0)
12400
    return(MagickTrue);
12401
  /*
12402
    Rotate image.
12403
  */
12404
  normalized_degrees=degrees;
12405
  while (normalized_degrees < -45.0)
12406
    normalized_degrees+=360.0;
12407
  for (rotations=0; normalized_degrees > 45.0; rotations++)
12408
    normalized_degrees-=90.0;
12409
  if (normalized_degrees != 0.0)
12410
    (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12411
      exception);
12412
  XSetCursorState(display,windows,MagickTrue);
12413
  XCheckRefreshWindows(display,windows);
12414
  (*image)->background_color.red=(double) ScaleShortToQuantum(
12415
    windows->pixel_info->pen_colors[pen_id].red);
12416
  (*image)->background_color.green=(double) ScaleShortToQuantum(
12417
    windows->pixel_info->pen_colors[pen_id].green);
12418
  (*image)->background_color.blue=(double) ScaleShortToQuantum(
12419
    windows->pixel_info->pen_colors[pen_id].blue);
12420
  rotate_image=RotateImage(*image,degrees,exception);
12421
  XSetCursorState(display,windows,MagickFalse);
12422
  if (rotate_image == (Image *) NULL)
12423
    return(MagickFalse);
12424
  *image=DestroyImage(*image);
12425
  *image=rotate_image;
12426
  if (windows->image.crop_geometry != (char *) NULL)
12427
    {
12428
      /*
12429
        Rotate crop geometry.
12430
      */
12431
      width=(unsigned int) (*image)->columns;
12432
      height=(unsigned int) (*image)->rows;
12433
      (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12434
      switch (rotations % 4)
12435
      {
12436
        default:
12437
        case 0:
12438
          break;
12439
        case 1:
12440
        {
12441
          /*
12442
            Rotate 90 degrees.
12443
          */
12444
          (void) FormatLocaleString(windows->image.crop_geometry,
12445
            MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
12446
            (int) height-y,x);
12447
          break;
12448
        }
12449
        case 2:
12450
        {
12451
          /*
12452
            Rotate 180 degrees.
12453
          */
12454
          (void) FormatLocaleString(windows->image.crop_geometry,
12455
            MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12456
            height-y);
12457
          break;
12458
        }
12459
        case 3:
12460
        {
12461
          /*
12462
            Rotate 270 degrees.
12463
          */
12464
          (void) FormatLocaleString(windows->image.crop_geometry,
12465
            MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12466
            (int) width-x);
12467
          break;
12468
        }
12469
      }
12470
    }
12471
  if (windows->image.orphan != MagickFalse)
12472
    return(MagickTrue);
12473
  if (normalized_degrees != 0.0)
12474
    {
12475
      /*
12476
        Update image colormap.
12477
      */
12478
      windows->image.window_changes.width=(int) (*image)->columns;
12479
      windows->image.window_changes.height=(int) (*image)->rows;
12480
      if (windows->image.crop_geometry != (char *) NULL)
12481
        {
12482
          /*
12483
            Obtain dimensions of image from crop geometry.
12484
          */
12485
          (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12486
            &width,&height);
12487
          windows->image.window_changes.width=(int) width;
12488
          windows->image.window_changes.height=(int) height;
12489
        }
12490
      XConfigureImageColormap(display,resource_info,windows,*image,exception);
12491
    }
12492
  else
12493
    if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12494
      {
12495
        windows->image.window_changes.width=windows->image.ximage->height;
12496
        windows->image.window_changes.height=windows->image.ximage->width;
12497
      }
12498
  /*
12499
    Update image configuration.
12500
  */
12501
  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12502
  return(MagickTrue);
12503
}
12504

12505
/*
12506
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12507
%                                                                             %
12508
%                                                                             %
12509
%                                                                             %
12510
+   X S a v e I m a g e                                                       %
12511
%                                                                             %
12512
%                                                                             %
12513
%                                                                             %
12514
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12515
%
12516
%  XSaveImage() saves an image to a file.
12517
%
12518
%  The format of the XSaveImage method is:
12519
%
12520
%      MagickBooleanType XSaveImage(Display *display,
12521
%        XResourceInfo *resource_info,XWindows *windows,Image *image,
12522
%        ExceptionInfo *exception)
12523
%
12524
%  A description of each parameter follows:
12525
%
12526
%    o display: Specifies a connection to an X server; returned from
12527
%      XOpenDisplay.
12528
%
12529
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12530
%
12531
%    o windows: Specifies a pointer to a XWindows structure.
12532
%
12533
%    o image: the image.
12534
%
12535
%    o exception: return any errors or warnings in this structure.
12536
%
12537
*/
12538
static MagickBooleanType XSaveImage(Display *display,
12539
  XResourceInfo *resource_info,XWindows *windows,Image *image,
12540
  ExceptionInfo *exception)
12541
{
12542
  char
12543
    filename[MagickPathExtent],
12544
    geometry[MagickPathExtent];
12545
12546
  Image
12547
    *save_image;
12548
12549
  ImageInfo
12550
    *image_info;
12551
12552
  MagickStatusType
12553
    status;
12554
12555
  /*
12556
    Request file name from user.
12557
  */
12558
  if (resource_info->write_filename != (char *) NULL)
12559
    (void) CopyMagickString(filename,resource_info->write_filename,
12560
      MagickPathExtent);
12561
  else
12562
    {
12563
      char
12564
        path[MagickPathExtent];
12565
12566
      int
12567
        status;
12568
12569
      GetPathComponent(image->filename,HeadPath,path);
12570
      GetPathComponent(image->filename,TailPath,filename);
12571
      if (*path != '\0')
12572
        {
12573
          status=chdir(path);
12574
          if (status == -1)
12575
            (void) ThrowMagickException(exception,GetMagickModule(),
12576
              FileOpenError,"UnableToOpenFile","%s",path);
12577
        }
12578
    }
12579
  XFileBrowserWidget(display,windows,"Save",filename);
12580
  if (*filename == '\0')
12581
    return(MagickTrue);
12582
  if (IsPathAccessible(filename) != MagickFalse)
12583
    {
12584
      int
12585
        status;
12586
12587
      /*
12588
        File exists-- seek user's permission before overwriting.
12589
      */
12590
      status=XConfirmWidget(display,windows,"Overwrite",filename);
12591
      if (status <= 0)
12592
        return(MagickTrue);
12593
    }
12594
  image_info=CloneImageInfo(resource_info->image_info);
12595
  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12596
  (void) SetImageInfo(image_info,1,exception);
12597
  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12598
      (LocaleCompare(image_info->magick,"JPG") == 0))
12599
    {
12600
      char
12601
        quality[MagickPathExtent];
12602
12603
      int
12604
        status;
12605
12606
      /*
12607
        Request JPEG quality from user.
12608
      */
12609
      (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12610
        image->quality);
12611
      status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12612
        quality);
12613
      if (*quality == '\0')
12614
        return(MagickTrue);
12615
      image->quality=StringToUnsignedLong(quality);
12616
      image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12617
    }
12618
  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12619
      (LocaleCompare(image_info->magick,"PDF") == 0) ||
12620
      (LocaleCompare(image_info->magick,"PS") == 0) ||
12621
      (LocaleCompare(image_info->magick,"PS2") == 0))
12622
    {
12623
      char
12624
        geometry[MagickPathExtent];
12625
12626
      const char
12627
        *const PageSizes[] =
12628
        {
12629
          "Letter",
12630
          "Tabloid",
12631
          "Ledger",
12632
          "Legal",
12633
          "Statement",
12634
          "Executive",
12635
          "A3",
12636
          "A4",
12637
          "A5",
12638
          "B4",
12639
          "B5",
12640
          "Folio",
12641
          "Quarto",
12642
          "10x14",
12643
          (char *) NULL
12644
        };
12645
12646
      /*
12647
        Request page geometry from user.
12648
      */
12649
      (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12650
      if (LocaleCompare(image_info->magick,"PDF") == 0)
12651
        (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12652
      if (image_info->page != (char *) NULL)
12653
        (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12654
      XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12655
        "Select page geometry:",geometry);
12656
      if (*geometry != '\0')
12657
        image_info->page=GetPageGeometry(geometry);
12658
    }
12659
  /*
12660
    Apply image transforms.
12661
  */
12662
  XSetCursorState(display,windows,MagickTrue);
12663
  XCheckRefreshWindows(display,windows);
12664
  save_image=CloneImage(image,0,0,MagickTrue,exception);
12665
  if (save_image == (Image *) NULL)
12666
    return(MagickFalse);
12667
  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12668
    windows->image.ximage->width,windows->image.ximage->height);
12669
  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12670
    exception);
12671
  /*
12672
    Write image.
12673
  */
12674
  (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12675
  status=WriteImage(image_info,save_image,exception);
12676
  if (status != MagickFalse)
12677
    image->taint=MagickFalse;
12678
  save_image=DestroyImage(save_image);
12679
  image_info=DestroyImageInfo(image_info);
12680
  XSetCursorState(display,windows,MagickFalse);
12681
  return(status != 0 ? MagickTrue : MagickFalse);
12682
}
12683

12684
/*
12685
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12686
%                                                                             %
12687
%                                                                             %
12688
%                                                                             %
12689
+   X S c r e e n E v e n t                                                   %
12690
%                                                                             %
12691
%                                                                             %
12692
%                                                                             %
12693
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12694
%
12695
%  XScreenEvent() handles global events associated with the Pan and Magnify
12696
%  windows.
12697
%
12698
%  The format of the XScreenEvent function is:
12699
%
12700
%      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12701
%        ExceptionInfo *exception)
12702
%
12703
%  A description of each parameter follows:
12704
%
12705
%    o display: Specifies a pointer to the Display structure;  returned from
12706
%      XOpenDisplay.
12707
%
12708
%    o windows: Specifies a pointer to a XWindows structure.
12709
%
12710
%    o event: Specifies a pointer to a X11 XEvent structure.
12711
%
12712
%    o exception: return any errors or warnings in this structure.
12713
%
12714
*/
12715
12716
#if defined(__cplusplus) || defined(c_plusplus)
12717
extern "C" {
12718
#endif
12719
12720
static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12721
{
12722
  XWindows
12723
    *windows;
12724
12725
  windows=(XWindows *) data;
12726
  if ((event->type == ClientMessage) &&
12727
      (event->xclient.window == windows->image.id))
12728
    return(MagickFalse);
12729
  return(MagickTrue);
12730
}
12731
12732
#if defined(__cplusplus) || defined(c_plusplus)
12733
}
12734
#endif
12735
12736
static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12737
  ExceptionInfo *exception)
12738
{
12739
  int
12740
    x,
12741
    y;
12742
12743
  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12744
  if (event->xany.window == windows->command.id)
12745
    return;
12746
  switch (event->type)
12747
  {
12748
    case ButtonPress:
12749
    case ButtonRelease:
12750
    {
12751
      if ((event->xbutton.button == Button3) &&
12752
          (event->xbutton.state & Mod1Mask))
12753
        {
12754
          /*
12755
            Convert Alt-Button3 to Button2.
12756
          */
12757
          event->xbutton.button=Button2;
12758
          event->xbutton.state&=(unsigned int) (~Mod1Mask);
12759
        }
12760
      if (event->xbutton.window == windows->backdrop.id)
12761
        {
12762
          (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12763
            event->xbutton.time);
12764
          break;
12765
        }
12766
      if (event->xbutton.window == windows->pan.id)
12767
        {
12768
          XPanImage(display,windows,event,exception);
12769
          break;
12770
        }
12771
      if (event->xbutton.window == windows->image.id)
12772
        if (event->xbutton.button == Button2)
12773
          {
12774
            /*
12775
              Update magnified image.
12776
            */
12777
            x=event->xbutton.x;
12778
            y=event->xbutton.y;
12779
            if (x < 0)
12780
              x=0;
12781
            else
12782
              if (x >= (int) windows->image.width)
12783
                x=(int) (windows->image.width-1);
12784
            windows->magnify.x=(int) windows->image.x+x;
12785
            if (y < 0)
12786
              y=0;
12787
            else
12788
             if (y >= (int) windows->image.height)
12789
               y=(int) (windows->image.height-1);
12790
            windows->magnify.y=windows->image.y+y;
12791
            if (windows->magnify.mapped == MagickFalse)
12792
              (void) XMapRaised(display,windows->magnify.id);
12793
            XMakeMagnifyImage(display,windows,exception);
12794
            if (event->type == ButtonRelease)
12795
              (void) XWithdrawWindow(display,windows->info.id,
12796
                windows->info.screen);
12797
            break;
12798
          }
12799
      break;
12800
    }
12801
    case ClientMessage:
12802
    {
12803
      /*
12804
        If client window delete message, exit.
12805
      */
12806
      if (event->xclient.message_type != windows->wm_protocols)
12807
        break;
12808
      if (*event->xclient.data.l != (long) windows->wm_delete_window)
12809
        break;
12810
      if (event->xclient.window == windows->magnify.id)
12811
        {
12812
          (void) XWithdrawWindow(display,windows->magnify.id,
12813
            windows->magnify.screen);
12814
          break;
12815
        }
12816
      break;
12817
    }
12818
    case ConfigureNotify:
12819
    {
12820
      if (event->xconfigure.window == windows->magnify.id)
12821
        {
12822
          unsigned int
12823
            magnify;
12824
12825
          /*
12826
            Magnify window has a new configuration.
12827
          */
12828
          windows->magnify.width=(unsigned int) event->xconfigure.width;
12829
          windows->magnify.height=(unsigned int) event->xconfigure.height;
12830
          if (windows->magnify.mapped == MagickFalse)
12831
            break;
12832
          magnify=1;
12833
          while ((int) magnify <= event->xconfigure.width)
12834
            magnify<<=1;
12835
          while ((int) magnify <= event->xconfigure.height)
12836
            magnify<<=1;
12837
          magnify>>=1;
12838
          if (((int) magnify != event->xconfigure.width) ||
12839
              ((int) magnify != event->xconfigure.height))
12840
            {
12841
              XWindowChanges
12842
                window_changes;
12843
12844
              window_changes.width=(int) magnify;
12845
              window_changes.height=(int) magnify;
12846
              (void) XReconfigureWMWindow(display,windows->magnify.id,
12847
                windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12848
                &window_changes);
12849
              break;
12850
            }
12851
          XMakeMagnifyImage(display,windows,exception);
12852
          break;
12853
        }
12854
      break;
12855
    }
12856
    case Expose:
12857
    {
12858
      if (event->xexpose.window == windows->image.id)
12859
        {
12860
          XRefreshWindow(display,&windows->image,event);
12861
          break;
12862
        }
12863
      if (event->xexpose.window == windows->pan.id)
12864
        if (event->xexpose.count == 0)
12865
          {
12866
            XDrawPanRectangle(display,windows);
12867
            break;
12868
          }
12869
      if (event->xexpose.window == windows->magnify.id)
12870
        if (event->xexpose.count == 0)
12871
          {
12872
            XMakeMagnifyImage(display,windows,exception);
12873
            break;
12874
          }
12875
      break;
12876
    }
12877
    case KeyPress:
12878
    {
12879
      char
12880
        command[MagickPathExtent];
12881
12882
      KeySym
12883
        key_symbol;
12884
12885
      if (event->xkey.window != windows->magnify.id)
12886
        break;
12887
      /*
12888
        Respond to a user key press.
12889
      */
12890
      (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12891
        sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12892
      XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12893
        exception);
12894
      break;
12895
    }
12896
    case MapNotify:
12897
    {
12898
      if (event->xmap.window == windows->magnify.id)
12899
        {
12900
          windows->magnify.mapped=MagickTrue;
12901
          (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12902
          break;
12903
        }
12904
      if (event->xmap.window == windows->info.id)
12905
        {
12906
          windows->info.mapped=MagickTrue;
12907
          break;
12908
        }
12909
      break;
12910
    }
12911
    case MotionNotify:
12912
    {
12913
      while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12914
      if (event->xmotion.window == windows->image.id)
12915
        if (windows->magnify.mapped != MagickFalse)
12916
          {
12917
            /*
12918
              Update magnified image.
12919
            */
12920
            x=event->xmotion.x;
12921
            y=event->xmotion.y;
12922
            if (x < 0)
12923
              x=0;
12924
            else
12925
              if (x >= (int) windows->image.width)
12926
                x=(int) (windows->image.width-1);
12927
            windows->magnify.x=(int) windows->image.x+x;
12928
            if (y < 0)
12929
              y=0;
12930
            else
12931
             if (y >= (int) windows->image.height)
12932
               y=(int) (windows->image.height-1);
12933
            windows->magnify.y=windows->image.y+y;
12934
            XMakeMagnifyImage(display,windows,exception);
12935
          }
12936
      break;
12937
    }
12938
    case UnmapNotify:
12939
    {
12940
      if (event->xunmap.window == windows->magnify.id)
12941
        {
12942
          windows->magnify.mapped=MagickFalse;
12943
          break;
12944
        }
12945
      if (event->xunmap.window == windows->info.id)
12946
        {
12947
          windows->info.mapped=MagickFalse;
12948
          break;
12949
        }
12950
      break;
12951
    }
12952
    default:
12953
      break;
12954
  }
12955
}
12956

12957
/*
12958
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12959
%                                                                             %
12960
%                                                                             %
12961
%                                                                             %
12962
+   X S e t C r o p G e o m e t r y                                           %
12963
%                                                                             %
12964
%                                                                             %
12965
%                                                                             %
12966
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12967
%
12968
%  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12969
%  and translates it to a cropping geometry relative to the image.
12970
%
12971
%  The format of the XSetCropGeometry method is:
12972
%
12973
%      void XSetCropGeometry(Display *display,XWindows *windows,
12974
%        RectangleInfo *crop_info,Image *image)
12975
%
12976
%  A description of each parameter follows:
12977
%
12978
%    o display: Specifies a connection to an X server; returned from
12979
%      XOpenDisplay.
12980
%
12981
%    o windows: Specifies a pointer to a XWindows structure.
12982
%
12983
%    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12984
%      Image window to crop.
12985
%
12986
%    o image: the image.
12987
%
12988
*/
12989
static void XSetCropGeometry(Display *display,XWindows *windows,
12990
  RectangleInfo *crop_info,Image *image)
12991
{
12992
  char
12993
    text[MagickPathExtent];
12994
12995
  int
12996
    x,
12997
    y;
12998
12999
  double
13000
    scale_factor;
13001
13002
  unsigned int
13003
    height,
13004
    width;
13005
13006
  if (windows->info.mapped != MagickFalse)
13007
    {
13008
      /*
13009
        Display info on cropping rectangle.
13010
      */
13011
      (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
13012
        (double) crop_info->width,(double) crop_info->height,(double)
13013
        crop_info->x,(double) crop_info->y);
13014
      XInfoWidget(display,windows,text);
13015
    }
13016
  /*
13017
    Cropping geometry is relative to any previous crop geometry.
13018
  */
13019
  x=0;
13020
  y=0;
13021
  width=(unsigned int) image->columns;
13022
  height=(unsigned int) image->rows;
13023
  if (windows->image.crop_geometry != (char *) NULL)
13024
    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13025
  else
13026
    windows->image.crop_geometry=AcquireString((char *) NULL);
13027
  /*
13028
    Define the crop geometry string from the cropping rectangle.
13029
  */
13030
  scale_factor=(double) width/windows->image.ximage->width;
13031
  if (crop_info->x > 0)
13032
    x+=(int) (scale_factor*crop_info->x+0.5);
13033
  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13034
  if (width == 0)
13035
    width=1;
13036
  scale_factor=(double) height/windows->image.ximage->height;
13037
  if (crop_info->y > 0)
13038
    y+=(int) (scale_factor*crop_info->y+0.5);
13039
  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13040
  if (height == 0)
13041
    height=1;
13042
  (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13043
    "%ux%u%+d%+d",width,height,x,y);
13044
}
13045

13046
/*
13047
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13048
%                                                                             %
13049
%                                                                             %
13050
%                                                                             %
13051
+   X T i l e I m a g e                                                       %
13052
%                                                                             %
13053
%                                                                             %
13054
%                                                                             %
13055
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13056
%
13057
%  XTileImage() loads or deletes a selected tile from a visual image directory.
13058
%  The load or delete command is chosen from a menu.
13059
%
13060
%  The format of the XTileImage method is:
13061
%
13062
%      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13063
%        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13064
%
13065
%  A description of each parameter follows:
13066
%
13067
%    o tile_image:  XTileImage reads or deletes the tile image
13068
%      and returns it.  A null image is returned if an error occurs.
13069
%
13070
%    o display: Specifies a connection to an X server;  returned from
13071
%      XOpenDisplay.
13072
%
13073
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13074
%
13075
%    o windows: Specifies a pointer to a XWindows structure.
13076
%
13077
%    o image: the image; returned from ReadImage.
13078
%
13079
%    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13080
%      the entire image is refreshed.
13081
%
13082
%    o exception: return any errors or warnings in this structure.
13083
%
13084
*/
13085
static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13086
  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13087
{
13088
  const char
13089
    *const VerbMenu[] =
13090
    {
13091
      "Load",
13092
      "Next",
13093
      "Former",
13094
      "Delete",
13095
      "Update",
13096
      (char *) NULL,
13097
    };
13098
13099
  static const ModeType
13100
    TileCommands[] =
13101
    {
13102
      TileLoadCommand,
13103
      TileNextCommand,
13104
      TileFormerCommand,
13105
      TileDeleteCommand,
13106
      TileUpdateCommand
13107
    };
13108
13109
  char
13110
    command[MagickPathExtent],
13111
    filename[MagickPathExtent];
13112
13113
  Image
13114
    *tile_image;
13115
13116
  int
13117
    id,
13118
    status,
13119
    tile,
13120
    x,
13121
    y;
13122
13123
  double
13124
    scale_factor;
13125
13126
  char
13127
    *p,
13128
    *q;
13129
13130
  int
13131
    i;
13132
13133
  unsigned int
13134
    height,
13135
    width;
13136
13137
  /*
13138
    Tile image is relative to montage image configuration.
13139
  */
13140
  x=0;
13141
  y=0;
13142
  width=(unsigned int) image->columns;
13143
  height=(unsigned int) image->rows;
13144
  if (windows->image.crop_geometry != (char *) NULL)
13145
    (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13146
  scale_factor=(double) width/windows->image.ximage->width;
13147
  event->xbutton.x+=windows->image.x;
13148
  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13149
  scale_factor=(double) height/windows->image.ximage->height;
13150
  event->xbutton.y+=windows->image.y;
13151
  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13152
  /*
13153
    Determine size and location of each tile in the visual image directory.
13154
  */
13155
  width=(unsigned int) image->columns;
13156
  height=(unsigned int) image->rows;
13157
  x=0;
13158
  y=0;
13159
  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13160
  tile=((event->xbutton.y-y)/(int) height)*(((int) image->columns-x)/(int)
13161
    width)+(event->xbutton.x-x)/(int) width;
13162
  if (tile < 0)
13163
    {
13164
      /*
13165
        Button press is outside any tile.
13166
      */
13167
      (void) XBell(display,0);
13168
      return((Image *) NULL);
13169
    }
13170
  /*
13171
    Determine file name from the tile directory.
13172
  */
13173
  p=image->directory;
13174
  for (i=tile; (i != 0) && (*p != '\0'); )
13175
  {
13176
    if (*p == '\xff')
13177
      i--;
13178
    p++;
13179
  }
13180
  if (*p == '\0')
13181
    {
13182
      /*
13183
        Button press is outside any tile.
13184
      */
13185
      (void) XBell(display,0);
13186
      return((Image *) NULL);
13187
    }
13188
  /*
13189
    Select a command from the pop-up menu.
13190
  */
13191
  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13192
  if (id < 0)
13193
    return((Image *) NULL);
13194
  q=p;
13195
  while ((*q != '\xff') && (*q != '\0') &&
13196
         ((size_t) (q-p+1) < sizeof(filename)))
13197
    q++;
13198
  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13199
  /*
13200
    Perform command for the selected tile.
13201
  */
13202
  XSetCursorState(display,windows,MagickTrue);
13203
  XCheckRefreshWindows(display,windows);
13204
  tile_image=NewImageList();
13205
  switch (TileCommands[id])
13206
  {
13207
    case TileLoadCommand:
13208
    {
13209
      /*
13210
        Load tile image.
13211
      */
13212
      XCheckRefreshWindows(display,windows);
13213
      (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13214
        MagickPathExtent);
13215
      (void) CopyMagickString(resource_info->image_info->filename,filename,
13216
        MagickPathExtent);
13217
      tile_image=ReadImage(resource_info->image_info,exception);
13218
      CatchException(exception);
13219
      (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13220
      break;
13221
    }
13222
    case TileNextCommand:
13223
    {
13224
      /*
13225
        Display next image.
13226
      */
13227
      XClientMessage(display,windows->image.id,windows->im_protocols,
13228
        windows->im_next_image,CurrentTime);
13229
      break;
13230
    }
13231
    case TileFormerCommand:
13232
    {
13233
      /*
13234
        Display former image.
13235
      */
13236
      XClientMessage(display,windows->image.id,windows->im_protocols,
13237
        windows->im_former_image,CurrentTime);
13238
      break;
13239
    }
13240
    case TileDeleteCommand:
13241
    {
13242
      /*
13243
        Delete tile image.
13244
      */
13245
      if (IsPathAccessible(filename) == MagickFalse)
13246
        {
13247
          XNoticeWidget(display,windows,"Image file does not exist:",filename);
13248
          break;
13249
        }
13250
      status=XConfirmWidget(display,windows,"Really delete tile",filename);
13251
      if (status <= 0)
13252
        break;
13253
      status=ShredFile(filename) == MagickFalse ? 0 : 1;
13254
      status|=remove_utf8(filename);
13255
      if (status != MagickFalse)
13256
        {
13257
          XNoticeWidget(display,windows,"Unable to delete image file:",
13258
            filename);
13259
          break;
13260
        }
13261
      magick_fallthrough;
13262
    }
13263
    case TileUpdateCommand:
13264
    {
13265
      int
13266
        x_offset,
13267
        y_offset;
13268
13269
      PixelInfo
13270
        pixel;
13271
13272
      int
13273
        j;
13274
13275
      Quantum
13276
        *s;
13277
13278
      /*
13279
        Ensure all the images exist.
13280
      */
13281
      tile=0;
13282
      GetPixelInfo(image,&pixel);
13283
      for (p=image->directory; *p != '\0'; p++)
13284
      {
13285
        CacheView
13286
          *image_view;
13287
13288
        q=p;
13289
        while ((*q != '\xff') && (*q != '\0') &&
13290
               ((size_t) (q-p+1) < sizeof(filename)))
13291
          q++;
13292
        (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13293
        p=q;
13294
        if (IsPathAccessible(filename) != MagickFalse)
13295
          {
13296
            tile++;
13297
            continue;
13298
          }
13299
        /*
13300
          Overwrite tile with background color.
13301
        */
13302
        x_offset=((int) width*(tile % (((int) image->columns-x)/(int) width))+
13303
          x);
13304
        y_offset=((int) height*(tile/(((int) image->columns-x)/(int) width))+
13305
          y);
13306
        image_view=AcquireAuthenticCacheView(image,exception);
13307
        (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13308
        for (i=0; i < (int) height; i++)
13309
        {
13310
          s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13311
            y_offset+i,width,1,exception);
13312
          if (s == (Quantum *) NULL)
13313
            break;
13314
          for (j=0; j < (int) width; j++)
13315
          {
13316
            SetPixelViaPixelInfo(image,&pixel,s);
13317
            s+=(ptrdiff_t) GetPixelChannels(image);
13318
          }
13319
          if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13320
            break;
13321
        }
13322
        image_view=DestroyCacheView(image_view);
13323
        tile++;
13324
      }
13325
      windows->image.window_changes.width=(int) image->columns;
13326
      windows->image.window_changes.height=(int) image->rows;
13327
      XConfigureImageColormap(display,resource_info,windows,image,exception);
13328
      (void) XConfigureImage(display,resource_info,windows,image,exception);
13329
      break;
13330
    }
13331
    default:
13332
      break;
13333
  }
13334
  XSetCursorState(display,windows,MagickFalse);
13335
  return(tile_image);
13336
}
13337

13338
/*
13339
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13340
%                                                                             %
13341
%                                                                             %
13342
%                                                                             %
13343
+   X T r a n s l a t e I m a g e                                             %
13344
%                                                                             %
13345
%                                                                             %
13346
%                                                                             %
13347
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13348
%
13349
%  XTranslateImage() translates the image within an Image window by one pixel
13350
%  as specified by the key symbol.  If the image has a montage string the
13351
%  translation is respect to the width and height contained within the string.
13352
%
13353
%  The format of the XTranslateImage method is:
13354
%
13355
%      void XTranslateImage(Display *display,XWindows *windows,
13356
%        Image *image,const KeySym key_symbol)
13357
%
13358
%  A description of each parameter follows:
13359
%
13360
%    o display: Specifies a connection to an X server; returned from
13361
%      XOpenDisplay.
13362
%
13363
%    o windows: Specifies a pointer to a XWindows structure.
13364
%
13365
%    o image: the image.
13366
%
13367
%    o key_symbol: Specifies a KeySym which indicates which side of the image
13368
%      to trim.
13369
%
13370
*/
13371
static void XTranslateImage(Display *display,XWindows *windows,
13372
  Image *image,const KeySym key_symbol)
13373
{
13374
  char
13375
    text[MagickPathExtent];
13376
13377
  int
13378
    x,
13379
    y;
13380
13381
  unsigned int
13382
    x_offset,
13383
    y_offset;
13384
13385
  /*
13386
    User specified a pan position offset.
13387
  */
13388
  x_offset=windows->image.width;
13389
  y_offset=windows->image.height;
13390
  if (image->montage != (char *) NULL)
13391
    (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13392
  switch ((int) key_symbol)
13393
  {
13394
    case XK_Home:
13395
    case XK_KP_Home:
13396
    {
13397
      windows->image.x=(int) windows->image.width/2;
13398
      windows->image.y=(int) windows->image.height/2;
13399
      break;
13400
    }
13401
    case XK_Left:
13402
    case XK_KP_Left:
13403
    {
13404
      windows->image.x-=(int) x_offset;
13405
      break;
13406
    }
13407
    case XK_Next:
13408
    case XK_Up:
13409
    case XK_KP_Up:
13410
    {
13411
      windows->image.y-=(int) y_offset;
13412
      break;
13413
    }
13414
    case XK_Right:
13415
    case XK_KP_Right:
13416
    {
13417
      windows->image.x+=(int) x_offset;
13418
      break;
13419
    }
13420
    case XK_Prior:
13421
    case XK_Down:
13422
    case XK_KP_Down:
13423
    {
13424
      windows->image.y+=(int) y_offset;
13425
      break;
13426
    }
13427
    default:
13428
      return;
13429
  }
13430
  /*
13431
    Check boundary conditions.
13432
  */
13433
  if (windows->image.x < 0)
13434
    windows->image.x=0;
13435
  else
13436
    if ((windows->image.x+(int) windows->image.width) > windows->image.ximage->width)
13437
      windows->image.x=windows->image.ximage->width-(int) windows->image.width;
13438
  if (windows->image.y < 0)
13439
    windows->image.y=0;
13440
  else
13441
    if ((windows->image.y+(int) windows->image.height) > windows->image.ximage->height)
13442
      windows->image.y=windows->image.ximage->height-(int)
13443
        windows->image.height;
13444
  /*
13445
    Refresh Image window.
13446
  */
13447
  (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13448
    windows->image.width,windows->image.height,windows->image.x,
13449
    windows->image.y);
13450
  XInfoWidget(display,windows,text);
13451
  XCheckRefreshWindows(display,windows);
13452
  XDrawPanRectangle(display,windows);
13453
  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13454
  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13455
}
13456

13457
/*
13458
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13459
%                                                                             %
13460
%                                                                             %
13461
%                                                                             %
13462
+   X T r i m I m a g e                                                       %
13463
%                                                                             %
13464
%                                                                             %
13465
%                                                                             %
13466
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13467
%
13468
%  XTrimImage() trims the edges from the Image window.
13469
%
13470
%  The format of the XTrimImage method is:
13471
%
13472
%      MagickBooleanType XTrimImage(Display *display,
13473
%        XResourceInfo *resource_info,XWindows *windows,Image *image,
13474
%        ExceptionInfo *exception)
13475
%
13476
%  A description of each parameter follows:
13477
%
13478
%    o display: Specifies a connection to an X server; returned from
13479
%      XOpenDisplay.
13480
%
13481
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13482
%
13483
%    o windows: Specifies a pointer to a XWindows structure.
13484
%
13485
%    o image: the image.
13486
%
13487
%    o exception: return any errors or warnings in this structure.
13488
%
13489
*/
13490
static MagickBooleanType XTrimImage(Display *display,
13491
  XResourceInfo *resource_info,XWindows *windows,Image *image,
13492
  ExceptionInfo *exception)
13493
{
13494
  RectangleInfo
13495
    trim_info;
13496
13497
  int
13498
    x,
13499
    y;
13500
13501
  size_t
13502
    background,
13503
    pixel;
13504
13505
  /*
13506
    Trim edges from image.
13507
  */
13508
  XSetCursorState(display,windows,MagickTrue);
13509
  XCheckRefreshWindows(display,windows);
13510
  /*
13511
    Crop the left edge.
13512
  */
13513
  background=XGetPixel(windows->image.ximage,0,0);
13514
  trim_info.width=(size_t) windows->image.ximage->width;
13515
  for (x=0; x < windows->image.ximage->width; x++)
13516
  {
13517
    for (y=0; y < windows->image.ximage->height; y++)
13518
    {
13519
      pixel=XGetPixel(windows->image.ximage,x,y);
13520
      if (pixel != background)
13521
        break;
13522
    }
13523
    if (y < windows->image.ximage->height)
13524
      break;
13525
  }
13526
  trim_info.x=(ssize_t) x;
13527
  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13528
    {
13529
      XSetCursorState(display,windows,MagickFalse);
13530
      return(MagickFalse);
13531
    }
13532
  /*
13533
    Crop the right edge.
13534
  */
13535
  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13536
  for (x=windows->image.ximage->width-1; x != 0; x--)
13537
  {
13538
    for (y=0; y < windows->image.ximage->height; y++)
13539
    {
13540
      pixel=XGetPixel(windows->image.ximage,x,y);
13541
      if (pixel != background)
13542
        break;
13543
    }
13544
    if (y < windows->image.ximage->height)
13545
      break;
13546
  }
13547
  trim_info.width=(size_t) (x-trim_info.x+1);
13548
  /*
13549
    Crop the top edge.
13550
  */
13551
  background=XGetPixel(windows->image.ximage,0,0);
13552
  trim_info.height=(size_t) windows->image.ximage->height;
13553
  for (y=0; y < windows->image.ximage->height; y++)
13554
  {
13555
    for (x=0; x < windows->image.ximage->width; x++)
13556
    {
13557
      pixel=XGetPixel(windows->image.ximage,x,y);
13558
      if (pixel != background)
13559
        break;
13560
    }
13561
    if (x < windows->image.ximage->width)
13562
      break;
13563
  }
13564
  trim_info.y=(ssize_t) y;
13565
  /*
13566
    Crop the bottom edge.
13567
  */
13568
  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13569
  for (y=windows->image.ximage->height-1; y != 0; y--)
13570
  {
13571
    for (x=0; x < windows->image.ximage->width; x++)
13572
    {
13573
      pixel=XGetPixel(windows->image.ximage,x,y);
13574
      if (pixel != background)
13575
        break;
13576
    }
13577
    if (x < windows->image.ximage->width)
13578
      break;
13579
  }
13580
  trim_info.height=(size_t) (y-trim_info.y+1);
13581
  if (((unsigned int) trim_info.width != windows->image.width) ||
13582
      ((unsigned int) trim_info.height != windows->image.height))
13583
    {
13584
      /*
13585
        Reconfigure Image window as defined by the trimming rectangle.
13586
      */
13587
      XSetCropGeometry(display,windows,&trim_info,image);
13588
      windows->image.window_changes.width=(int) trim_info.width;
13589
      windows->image.window_changes.height=(int) trim_info.height;
13590
      (void) XConfigureImage(display,resource_info,windows,image,exception);
13591
    }
13592
  XSetCursorState(display,windows,MagickFalse);
13593
  return(MagickTrue);
13594
}
13595

13596
/*
13597
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13598
%                                                                             %
13599
%                                                                             %
13600
%                                                                             %
13601
+   X V i s u a l D i r e c t o r y I m a g e                                 %
13602
%                                                                             %
13603
%                                                                             %
13604
%                                                                             %
13605
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13606
%
13607
%  XVisualDirectoryImage() creates a Visual Image Directory.
13608
%
13609
%  The format of the XVisualDirectoryImage method is:
13610
%
13611
%      Image *XVisualDirectoryImage(Display *display,
13612
%        XResourceInfo *resource_info,XWindows *windows,
13613
%        ExceptionInfo *exception)
13614
%
13615
%  A description of each parameter follows:
13616
%
13617
%    o display: Specifies a connection to an X server; returned from
13618
%      XOpenDisplay.
13619
%
13620
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13621
%
13622
%    o windows: Specifies a pointer to a XWindows structure.
13623
%
13624
%    o exception: return any errors or warnings in this structure.
13625
%
13626
*/
13627
static Image *XVisualDirectoryImage(Display *display,
13628
  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13629
{
13630
#define TileImageTag  "Scale/Image"
13631
#define XClientName  "montage"
13632
13633
  char
13634
    **filelist;
13635
13636
  Image
13637
    *images,
13638
    *montage_image,
13639
    *next_image,
13640
    *thumbnail_image;
13641
13642
  ImageInfo
13643
    *read_info;
13644
13645
  int
13646
    number_files;
13647
13648
  MagickBooleanType
13649
    backdrop;
13650
13651
  MagickStatusType
13652
    status;
13653
13654
  MontageInfo
13655
    *montage_info;
13656
13657
  RectangleInfo
13658
    geometry;
13659
13660
  int
13661
    i;
13662
13663
  static char
13664
    filename[MagickPathExtent] = "\0",
13665
    filenames[MagickPathExtent] = "*";
13666
13667
  XResourceInfo
13668
    background_resources;
13669
13670
  /*
13671
    Request file name from user.
13672
  */
13673
  XFileBrowserWidget(display,windows,"Directory",filenames);
13674
  if (*filenames == '\0')
13675
    return((Image *) NULL);
13676
  /*
13677
    Expand the filenames.
13678
  */
13679
  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13680
  if (filelist == (char **) NULL)
13681
    {
13682
      ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13683
        filenames);
13684
      return((Image *) NULL);
13685
    }
13686
  number_files=1;
13687
  filelist[0]=filenames;
13688
  status=ExpandFilenames(&number_files,&filelist);
13689
  if ((status == MagickFalse) || (number_files == 0))
13690
    {
13691
      if (number_files == 0)
13692
        ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13693
      else
13694
        ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13695
          filenames);
13696
      return((Image *) NULL);
13697
    }
13698
  /*
13699
    Set image background resources.
13700
  */
13701
  background_resources=(*resource_info);
13702
  background_resources.window_id=AcquireString("");
13703
  (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13704
    "0x%lx",windows->image.id);
13705
  background_resources.backdrop=MagickTrue;
13706
  /*
13707
    Read each image and convert them to a tile.
13708
  */
13709
  backdrop=((windows->visual_info->klass == TrueColor) ||
13710
    (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13711
  read_info=CloneImageInfo(resource_info->image_info);
13712
  (void) SetImageOption(read_info,"jpeg:size","120x120");
13713
  (void) CloneString(&read_info->size,DefaultTileGeometry);
13714
  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13715
    (void *) NULL);
13716
  images=NewImageList();
13717
  XSetCursorState(display,windows,MagickTrue);
13718
  XCheckRefreshWindows(display,windows);
13719
  for (i=0; i < (int) number_files; i++)
13720
  {
13721
    (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13722
    filelist[i]=DestroyString(filelist[i]);
13723
    *read_info->magick='\0';
13724
    next_image=ReadImage(read_info,exception);
13725
    CatchException(exception);
13726
    if (next_image != (Image *) NULL)
13727
      {
13728
        (void) DeleteImageProperty(next_image,"label");
13729
        (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13730
          read_info,next_image,DefaultTileLabel,exception),exception);
13731
        (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13732
          exception);
13733
        thumbnail_image=ThumbnailImage(next_image,geometry.width,
13734
          geometry.height,exception);
13735
        if (thumbnail_image != (Image *) NULL)
13736
          {
13737
            next_image=DestroyImage(next_image);
13738
            next_image=thumbnail_image;
13739
          }
13740
        if (backdrop)
13741
          {
13742
            (void) XDisplayBackgroundImage(display,&background_resources,
13743
              next_image,exception);
13744
            XSetCursorState(display,windows,MagickTrue);
13745
          }
13746
        AppendImageToList(&images,next_image);
13747
        if (images->progress_monitor != (MagickProgressMonitor) NULL)
13748
          {
13749
            MagickBooleanType
13750
              proceed;
13751
13752
            proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13753
              (MagickSizeType) number_files);
13754
            if (proceed == MagickFalse)
13755
              break;
13756
          }
13757
      }
13758
  }
13759
  filelist=(char **) RelinquishMagickMemory(filelist);
13760
  if (images == (Image *) NULL)
13761
    {
13762
      read_info=DestroyImageInfo(read_info);
13763
      XSetCursorState(display,windows,MagickFalse);
13764
      ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13765
      return((Image *) NULL);
13766
    }
13767
  /*
13768
    Create the Visual Image Directory.
13769
  */
13770
  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13771
  montage_info->pointsize=10;
13772
  if (resource_info->font != (char *) NULL)
13773
    (void) CloneString(&montage_info->font,resource_info->font);
13774
  (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13775
  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13776
    images),exception);
13777
  images=DestroyImageList(images);
13778
  montage_info=DestroyMontageInfo(montage_info);
13779
  read_info=DestroyImageInfo(read_info);
13780
  XSetCursorState(display,windows,MagickFalse);
13781
  if (montage_image == (Image *) NULL)
13782
    return(montage_image);
13783
  XClientMessage(display,windows->image.id,windows->im_protocols,
13784
    windows->im_next_image,CurrentTime);
13785
  return(montage_image);
13786
}
13787

13788
/*
13789
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13790
%                                                                             %
13791
%                                                                             %
13792
%                                                                             %
13793
%   X D i s p l a y B a c k g r o u n d I m a g e                             %
13794
%                                                                             %
13795
%                                                                             %
13796
%                                                                             %
13797
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13798
%
13799
%  XDisplayBackgroundImage() displays an image in the background of a window.
13800
%
13801
%  The format of the XDisplayBackgroundImage method is:
13802
%
13803
%      MagickBooleanType XDisplayBackgroundImage(Display *display,
13804
%        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13805
%
13806
%  A description of each parameter follows:
13807
%
13808
%    o display: Specifies a connection to an X server;  returned from
13809
%      XOpenDisplay.
13810
%
13811
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13812
%
13813
%    o image: the image.
13814
%
13815
%    o exception: return any errors or warnings in this structure.
13816
%
13817
*/
13818
MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13819
  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13820
{
13821
  char
13822
    geometry[MagickPathExtent],
13823
    visual_type[MagickPathExtent];
13824
13825
  int
13826
    height,
13827
    status,
13828
    width;
13829
13830
  RectangleInfo
13831
    geometry_info;
13832
13833
  static XPixelInfo
13834
    pixel;
13835
13836
  static XStandardColormap
13837
    *map_info;
13838
13839
  static XVisualInfo
13840
    *visual_info = (XVisualInfo *) NULL;
13841
13842
  static XWindowInfo
13843
    window_info;
13844
13845
  size_t
13846
    delay;
13847
13848
  Window
13849
    root_window;
13850
13851
  XGCValues
13852
    context_values;
13853
13854
  XResourceInfo
13855
    resources;
13856
13857
  XWindowAttributes
13858
    window_attributes;
13859
13860
  /*
13861
    Determine target window.
13862
  */
13863
  assert(image != (Image *) NULL);
13864
  assert(image->signature == MagickCoreSignature);
13865
  if (IsEventLogging() != MagickFalse)
13866
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13867
  resources=(*resource_info);
13868
  window_info.id=(Window) NULL;
13869
  root_window=XRootWindow(display,XDefaultScreen(display));
13870
  if (LocaleCompare(resources.window_id,"root") == 0)
13871
    window_info.id=root_window;
13872
  else
13873
    {
13874
      if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13875
        window_info.id=XWindowByID(display,root_window,
13876
          (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13877
      if (window_info.id == (Window) NULL)
13878
        window_info.id=XWindowByName(display,root_window,resources.window_id);
13879
    }
13880
  if (window_info.id == (Window) NULL)
13881
    {
13882
      ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13883
        resources.window_id);
13884
      return(MagickFalse);
13885
    }
13886
  /*
13887
    Determine window visual id.
13888
  */
13889
  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13890
  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13891
  (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13892
  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13893
  if (status != 0)
13894
    (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13895
      XVisualIDFromVisual(window_attributes.visual));
13896
  if (visual_info == (XVisualInfo *) NULL)
13897
    {
13898
      /*
13899
        Allocate standard colormap.
13900
      */
13901
      map_info=XAllocStandardColormap();
13902
      if (map_info == (XStandardColormap *) NULL)
13903
        ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13904
          image->filename);
13905
      map_info->colormap=(Colormap) NULL;
13906
      pixel.pixels=(unsigned long *) NULL;
13907
      /*
13908
        Initialize visual info.
13909
      */
13910
      resources.map_type=(char *) NULL;
13911
      resources.visual_type=visual_type;
13912
      visual_info=XBestVisualInfo(display,map_info,&resources);
13913
      if (visual_info == (XVisualInfo *) NULL)
13914
        ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13915
          resources.visual_type);
13916
      /*
13917
        Initialize window info.
13918
      */
13919
      window_info.ximage=(XImage *) NULL;
13920
      window_info.matte_image=(XImage *) NULL;
13921
      window_info.pixmap=(Pixmap) NULL;
13922
      window_info.matte_pixmap=(Pixmap) NULL;
13923
    }
13924
  /*
13925
    Free previous root colors.
13926
  */
13927
  if (window_info.id == root_window)
13928
    (void) XDestroyWindowColors(display,root_window);
13929
  /*
13930
    Initialize Standard Colormap.
13931
  */
13932
  resources.colormap=SharedColormap;
13933
  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13934
    exception);
13935
  /*
13936
    Graphic context superclass.
13937
  */
13938
  context_values.background=pixel.foreground_color.pixel;
13939
  context_values.foreground=pixel.background_color.pixel;
13940
  pixel.annotate_context=XCreateGC(display,window_info.id,
13941
    (size_t) (GCBackground | GCForeground),&context_values);
13942
  if (pixel.annotate_context == (GC) NULL)
13943
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13944
      image->filename);
13945
  /*
13946
    Initialize Image window attributes.
13947
  */
13948
  window_info.name=AcquireString("\0");
13949
  window_info.icon_name=AcquireString("\0");
13950
  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13951
    &resources,&window_info);
13952
  /*
13953
    Create the X image.
13954
  */
13955
  window_info.width=(unsigned int) image->columns;
13956
  window_info.height=(unsigned int) image->rows;
13957
  if ((image->columns != window_info.width) ||
13958
      (image->rows != window_info.height))
13959
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13960
      image->filename);
13961
  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13962
    window_attributes.width,window_attributes.height);
13963
  geometry_info.width=window_info.width;
13964
  geometry_info.height=window_info.height;
13965
  geometry_info.x=(ssize_t) window_info.x;
13966
  geometry_info.y=(ssize_t) window_info.y;
13967
  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13968
    &geometry_info.width,&geometry_info.height);
13969
  window_info.width=(unsigned int) geometry_info.width;
13970
  window_info.height=(unsigned int) geometry_info.height;
13971
  window_info.x=(int) geometry_info.x;
13972
  window_info.y=(int) geometry_info.y;
13973
  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13974
    window_info.height,exception) == MagickFalse ? 0 : 1;
13975
  if (status == MagickFalse)
13976
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13977
      image->filename);
13978
  window_info.x=0;
13979
  window_info.y=0;
13980
  if (resource_info->debug != MagickFalse)
13981
    {
13982
      (void) LogMagickEvent(X11Event,GetMagickModule(),
13983
        "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13984
        (double) image->columns,(double) image->rows);
13985
      if (image->colors != 0)
13986
        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13987
          image->colors);
13988
      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13989
    }
13990
  /*
13991
    Adjust image dimensions as specified by backdrop or geometry options.
13992
  */
13993
  width=(int) window_info.width;
13994
  height=(int) window_info.height;
13995
  if (resources.backdrop != MagickFalse)
13996
    {
13997
      /*
13998
        Center image on window.
13999
      */
14000
      window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
14001
      window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
14002
      width=window_attributes.width;
14003
      height=window_attributes.height;
14004
    }
14005
  if ((resources.image_geometry != (char *) NULL) &&
14006
      (*resources.image_geometry != '\0'))
14007
    {
14008
      char
14009
        default_geometry[MagickPathExtent];
14010
14011
      int
14012
        flags,
14013
        gravity;
14014
14015
      XSizeHints
14016
        *size_hints;
14017
14018
      /*
14019
        User specified geometry.
14020
      */
14021
      size_hints=XAllocSizeHints();
14022
      if (size_hints == (XSizeHints *) NULL)
14023
        ThrowXWindowFatalException(ResourceLimitFatalError,
14024
          "MemoryAllocationFailed",image->filename);
14025
      size_hints->flags=0L;
14026
      (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
14027
        width,height);
14028
      flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14029
        default_geometry,window_info.border_width,size_hints,&window_info.x,
14030
        &window_info.y,&width,&height,&gravity);
14031
      if (flags & (XValue | YValue))
14032
        {
14033
          width=window_attributes.width;
14034
          height=window_attributes.height;
14035
        }
14036
      (void) XFree((void *) size_hints);
14037
    }
14038
  /*
14039
    Create the X pixmap.
14040
  */
14041
  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14042
    (unsigned int) height,window_info.depth);
14043
  if (window_info.pixmap == (Pixmap) NULL)
14044
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14045
      image->filename);
14046
  /*
14047
    Display pixmap on the window.
14048
  */
14049
  if (((unsigned int) width > window_info.width) ||
14050
      ((unsigned int) height > window_info.height))
14051
    (void) XFillRectangle(display,window_info.pixmap,
14052
      window_info.annotate_context,0,0,(unsigned int) width,
14053
      (unsigned int) height);
14054
  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14055
    window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14056
    window_info.width,(unsigned int) window_info.height);
14057
  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14058
  (void) XClearWindow(display,window_info.id);
14059
  delay=1000*image->delay/(size_t) MagickMax(image->ticks_per_second,1L);
14060
  XDelay(display,delay == 0UL ? 10UL : delay);
14061
  (void) XSync(display,MagickFalse);
14062
  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14063
}
14064

14065
/*
14066
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14067
%                                                                             %
14068
%                                                                             %
14069
%                                                                             %
14070
+   X D i s p l a y I m a g e                                                 %
14071
%                                                                             %
14072
%                                                                             %
14073
%                                                                             %
14074
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14075
%
14076
%  XDisplayImage() displays an image via X11.  A new image is created and
14077
%  returned if the user interactively transforms the displayed image.
14078
%
14079
%  The format of the XDisplayImage method is:
14080
%
14081
%      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14082
%        char **argv,int argc,Image **image,size_t *state,
14083
%        ExceptionInfo *exception)
14084
%
14085
%  A description of each parameter follows:
14086
%
14087
%    o nexus:  Method XDisplayImage returns an image when the
14088
%      user chooses 'Open Image' from the command menu or picks a tile
14089
%      from the image directory.  Otherwise a null image is returned.
14090
%
14091
%    o display: Specifies a connection to an X server;  returned from
14092
%      XOpenDisplay.
14093
%
14094
%    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14095
%
14096
%    o argv: Specifies the application's argument list.
14097
%
14098
%    o argc: Specifies the number of arguments.
14099
%
14100
%    o image: Specifies an address to an address of an Image structure;
14101
%
14102
%    o exception: return any errors or warnings in this structure.
14103
%
14104
*/
14105
MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14106
  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14107
{
14108
#define MagnifySize  256  /* must be a power of 2 */
14109
#define MagickMenus  10
14110
#define MagickTitle  "Commands"
14111
14112
  const char
14113
    *const CommandMenu[] =
14114
    {
14115
      "File",
14116
      "Edit",
14117
      "View",
14118
      "Transform",
14119
      "Enhance",
14120
      "Effects",
14121
      "F/X",
14122
      "Image Edit",
14123
      "Miscellany",
14124
      "Help",
14125
      (char *) NULL
14126
    },
14127
    *const FileMenu[] =
14128
    {
14129
      "Open...",
14130
      "Next",
14131
      "Former",
14132
      "Select...",
14133
      "Save...",
14134
      "Print...",
14135
      "Delete...",
14136
      "New...",
14137
      "Visual Directory...",
14138
      "Quit",
14139
      (char *) NULL
14140
    },
14141
    *const EditMenu[] =
14142
    {
14143
      "Undo",
14144
      "Redo",
14145
      "Cut",
14146
      "Copy",
14147
      "Paste",
14148
      (char *) NULL
14149
    },
14150
    *const ViewMenu[] =
14151
    {
14152
      "Half Size",
14153
      "Original Size",
14154
      "Double Size",
14155
      "Resize...",
14156
      "Apply",
14157
      "Refresh",
14158
      "Restore",
14159
      (char *) NULL
14160
    },
14161
    *const TransformMenu[] =
14162
    {
14163
      "Crop",
14164
      "Chop",
14165
      "Flop",
14166
      "Flip",
14167
      "Rotate Right",
14168
      "Rotate Left",
14169
      "Rotate...",
14170
      "Shear...",
14171
      "Roll...",
14172
      "Trim Edges",
14173
      (char *) NULL
14174
    },
14175
    *const EnhanceMenu[] =
14176
    {
14177
      "Hue...",
14178
      "Saturation...",
14179
      "Brightness...",
14180
      "Gamma...",
14181
      "Spiff",
14182
      "Dull",
14183
      "Contrast Stretch...",
14184
      "Sigmoidal Contrast...",
14185
      "Normalize",
14186
      "Equalize",
14187
      "Negate",
14188
      "Grayscale",
14189
      "Map...",
14190
      "Quantize...",
14191
      (char *) NULL
14192
    },
14193
    *const EffectsMenu[] =
14194
    {
14195
      "Despeckle",
14196
      "Emboss",
14197
      "Reduce Noise",
14198
      "Add Noise...",
14199
      "Sharpen...",
14200
      "Blur...",
14201
      "Threshold...",
14202
      "Edge Detect...",
14203
      "Spread...",
14204
      "Shade...",
14205
      "Raise...",
14206
      "Segment...",
14207
      (char *) NULL
14208
    },
14209
    *const FXMenu[] =
14210
    {
14211
      "Solarize...",
14212
      "Sepia Tone...",
14213
      "Swirl...",
14214
      "Implode...",
14215
      "Vignette...",
14216
      "Wave...",
14217
      "Oil Paint...",
14218
      "Charcoal Draw...",
14219
      (char *) NULL
14220
    },
14221
    *const ImageEditMenu[] =
14222
    {
14223
      "Annotate...",
14224
      "Draw...",
14225
      "Color...",
14226
      "Matte...",
14227
      "Composite...",
14228
      "Add Border...",
14229
      "Add Frame...",
14230
      "Comment...",
14231
      "Launch...",
14232
      "Region of Interest...",
14233
      (char *) NULL
14234
    },
14235
    *const MiscellanyMenu[] =
14236
    {
14237
      "Image Info",
14238
      "Zoom Image",
14239
      "Show Preview...",
14240
      "Show Histogram",
14241
      "Show Matte",
14242
      "Background...",
14243
      "Slide Show...",
14244
      "Preferences...",
14245
      (char *) NULL
14246
    },
14247
    *const HelpMenu[] =
14248
    {
14249
      "Overview",
14250
      "Browse Documentation",
14251
      "About Display",
14252
      (char *) NULL
14253
    },
14254
    *const ShortCutsMenu[] =
14255
    {
14256
      "Next",
14257
      "Former",
14258
      "Open...",
14259
      "Save...",
14260
      "Print...",
14261
      "Undo",
14262
      "Restore",
14263
      "Image Info",
14264
      "Quit",
14265
      (char *) NULL
14266
    },
14267
    *const VirtualMenu[] =
14268
    {
14269
      "Image Info",
14270
      "Print",
14271
      "Next",
14272
      "Quit",
14273
      (char *) NULL
14274
    };
14275
14276
  const char
14277
    *const *Menus[MagickMenus] =
14278
    {
14279
      FileMenu,
14280
      EditMenu,
14281
      ViewMenu,
14282
      TransformMenu,
14283
      EnhanceMenu,
14284
      EffectsMenu,
14285
      FXMenu,
14286
      ImageEditMenu,
14287
      MiscellanyMenu,
14288
      HelpMenu
14289
    };
14290
14291
  static DisplayCommand
14292
    CommandMenus[] =
14293
    {
14294
      NullCommand,
14295
      NullCommand,
14296
      NullCommand,
14297
      NullCommand,
14298
      NullCommand,
14299
      NullCommand,
14300
      NullCommand,
14301
      NullCommand,
14302
      NullCommand,
14303
      NullCommand,
14304
    },
14305
    FileCommands[] =
14306
    {
14307
      OpenCommand,
14308
      NextCommand,
14309
      FormerCommand,
14310
      SelectCommand,
14311
      SaveCommand,
14312
      PrintCommand,
14313
      DeleteCommand,
14314
      NewCommand,
14315
      VisualDirectoryCommand,
14316
      QuitCommand
14317
    },
14318
    EditCommands[] =
14319
    {
14320
      UndoCommand,
14321
      RedoCommand,
14322
      CutCommand,
14323
      CopyCommand,
14324
      PasteCommand
14325
    },
14326
    ViewCommands[] =
14327
    {
14328
      HalfSizeCommand,
14329
      OriginalSizeCommand,
14330
      DoubleSizeCommand,
14331
      ResizeCommand,
14332
      ApplyCommand,
14333
      RefreshCommand,
14334
      RestoreCommand
14335
    },
14336
    TransformCommands[] =
14337
    {
14338
      CropCommand,
14339
      ChopCommand,
14340
      FlopCommand,
14341
      FlipCommand,
14342
      RotateRightCommand,
14343
      RotateLeftCommand,
14344
      RotateCommand,
14345
      ShearCommand,
14346
      RollCommand,
14347
      TrimCommand
14348
    },
14349
    EnhanceCommands[] =
14350
    {
14351
      HueCommand,
14352
      SaturationCommand,
14353
      BrightnessCommand,
14354
      GammaCommand,
14355
      SpiffCommand,
14356
      DullCommand,
14357
      ContrastStretchCommand,
14358
      SigmoidalContrastCommand,
14359
      NormalizeCommand,
14360
      EqualizeCommand,
14361
      NegateCommand,
14362
      GrayscaleCommand,
14363
      MapCommand,
14364
      QuantizeCommand
14365
    },
14366
    EffectsCommands[] =
14367
    {
14368
      DespeckleCommand,
14369
      EmbossCommand,
14370
      ReduceNoiseCommand,
14371
      AddNoiseCommand,
14372
      SharpenCommand,
14373
      BlurCommand,
14374
      ThresholdCommand,
14375
      EdgeDetectCommand,
14376
      SpreadCommand,
14377
      ShadeCommand,
14378
      RaiseCommand,
14379
      SegmentCommand
14380
    },
14381
    FXCommands[] =
14382
    {
14383
      SolarizeCommand,
14384
      SepiaToneCommand,
14385
      SwirlCommand,
14386
      ImplodeCommand,
14387
      VignetteCommand,
14388
      WaveCommand,
14389
      OilPaintCommand,
14390
      CharcoalDrawCommand
14391
    },
14392
    ImageEditCommands[] =
14393
    {
14394
      AnnotateCommand,
14395
      DrawCommand,
14396
      ColorCommand,
14397
      MatteCommand,
14398
      CompositeCommand,
14399
      AddBorderCommand,
14400
      AddFrameCommand,
14401
      CommentCommand,
14402
      LaunchCommand,
14403
      RegionOfInterestCommand
14404
    },
14405
    MiscellanyCommands[] =
14406
    {
14407
      InfoCommand,
14408
      ZoomCommand,
14409
      ShowPreviewCommand,
14410
      ShowHistogramCommand,
14411
      ShowMatteCommand,
14412
      BackgroundCommand,
14413
      SlideShowCommand,
14414
      PreferencesCommand
14415
    },
14416
    HelpCommands[] =
14417
    {
14418
      HelpCommand,
14419
      BrowseDocumentationCommand,
14420
      VersionCommand
14421
    },
14422
    ShortCutsCommands[] =
14423
    {
14424
      NextCommand,
14425
      FormerCommand,
14426
      OpenCommand,
14427
      SaveCommand,
14428
      PrintCommand,
14429
      UndoCommand,
14430
      RestoreCommand,
14431
      InfoCommand,
14432
      QuitCommand
14433
    },
14434
    VirtualCommands[] =
14435
    {
14436
      InfoCommand,
14437
      PrintCommand,
14438
      NextCommand,
14439
      QuitCommand
14440
    };
14441
14442
  static DisplayCommand
14443
    *Commands[MagickMenus] =
14444
    {
14445
      FileCommands,
14446
      EditCommands,
14447
      ViewCommands,
14448
      TransformCommands,
14449
      EnhanceCommands,
14450
      EffectsCommands,
14451
      FXCommands,
14452
      ImageEditCommands,
14453
      MiscellanyCommands,
14454
      HelpCommands
14455
    };
14456
14457
  char
14458
    command[MagickPathExtent],
14459
    *directory,
14460
    geometry[MagickPathExtent],
14461
    resource_name[MagickPathExtent];
14462
14463
  DisplayCommand
14464
    display_command;
14465
14466
  Image
14467
    *display_image,
14468
    *nexus;
14469
14470
  int
14471
    entry,
14472
    id;
14473
14474
  KeySym
14475
    key_symbol;
14476
14477
  MagickStatusType
14478
    context_mask,
14479
    status;
14480
14481
  RectangleInfo
14482
    geometry_info;
14483
14484
  int
14485
    i;
14486
14487
  static char
14488
    working_directory[MagickPathExtent];
14489
14490
  static XPoint
14491
    vid_info;
14492
14493
  static XWindowInfo
14494
    *magick_windows[MaxXWindows];
14495
14496
  static unsigned int
14497
    number_windows;
14498
14499
  struct stat
14500
    attributes;
14501
14502
  time_t
14503
    timer,
14504
    timestamp,
14505
    update_time;
14506
14507
  unsigned int
14508
    height,
14509
    width;
14510
14511
  size_t
14512
    delay;
14513
14514
  WarningHandler
14515
    warning_handler;
14516
14517
  Window
14518
    root_window;
14519
14520
  XClassHint
14521
    *class_hints;
14522
14523
  XEvent
14524
    event;
14525
14526
  XFontStruct
14527
    *font_info;
14528
14529
  XGCValues
14530
    context_values;
14531
14532
  XPixelInfo
14533
    *icon_pixel,
14534
    *pixel;
14535
14536
  XResourceInfo
14537
    *icon_resources;
14538
14539
  XStandardColormap
14540
    *icon_map,
14541
    *map_info;
14542
14543
  XVisualInfo
14544
    *icon_visual,
14545
    *visual_info;
14546
14547
  XWindowChanges
14548
    window_changes;
14549
14550
  XWindows
14551
    *windows;
14552
14553
  XWMHints
14554
    *manager_hints;
14555
14556
  assert(image != (Image **) NULL);
14557
  assert((*image)->signature == MagickCoreSignature);
14558
  if (IsEventLogging() != MagickFalse)
14559
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14560
  display_image=(*image);
14561
  warning_handler=(WarningHandler) NULL;
14562
  windows=XSetWindows((XWindows *) ~0);
14563
  if (windows != (XWindows *) NULL)
14564
    {
14565
      int
14566
        status;
14567
14568
      if (*working_directory == '\0')
14569
        (void) CopyMagickString(working_directory,".",MagickPathExtent);
14570
      status=chdir(working_directory);
14571
      if (status == -1)
14572
        (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14573
          "UnableToOpenFile","%s",working_directory);
14574
      warning_handler=resource_info->display_warnings ?
14575
        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14576
      warning_handler=resource_info->display_warnings ?
14577
        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14578
    }
14579
  else
14580
    {
14581
      /*
14582
        Allocate windows structure.
14583
      */
14584
      resource_info->colors=display_image->colors;
14585
      windows=XSetWindows(XInitializeWindows(display,resource_info));
14586
      if (windows == (XWindows *) NULL)
14587
        ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14588
          (*image)->filename);
14589
      /*
14590
        Initialize window id's.
14591
      */
14592
      number_windows=0;
14593
      magick_windows[number_windows++]=(&windows->icon);
14594
      magick_windows[number_windows++]=(&windows->backdrop);
14595
      magick_windows[number_windows++]=(&windows->image);
14596
      magick_windows[number_windows++]=(&windows->info);
14597
      magick_windows[number_windows++]=(&windows->command);
14598
      magick_windows[number_windows++]=(&windows->widget);
14599
      magick_windows[number_windows++]=(&windows->popup);
14600
      magick_windows[number_windows++]=(&windows->magnify);
14601
      magick_windows[number_windows++]=(&windows->pan);
14602
      for (i=0; i < (int) number_windows; i++)
14603
        magick_windows[i]->id=(Window) NULL;
14604
      vid_info.x=0;
14605
      vid_info.y=0;
14606
    }
14607
  /*
14608
    Initialize font info.
14609
  */
14610
  if (windows->font_info != (XFontStruct *) NULL)
14611
    (void) XFreeFont(display,windows->font_info);
14612
  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14613
  if (windows->font_info == (XFontStruct *) NULL)
14614
    ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14615
      resource_info->font);
14616
  /*
14617
    Initialize Standard Colormap.
14618
  */
14619
  map_info=windows->map_info;
14620
  icon_map=windows->icon_map;
14621
  visual_info=windows->visual_info;
14622
  icon_visual=windows->icon_visual;
14623
  pixel=windows->pixel_info;
14624
  icon_pixel=windows->icon_pixel;
14625
  font_info=windows->font_info;
14626
  icon_resources=windows->icon_resources;
14627
  class_hints=windows->class_hints;
14628
  manager_hints=windows->manager_hints;
14629
  root_window=XRootWindow(display,visual_info->screen);
14630
  nexus=NewImageList();
14631
  if (resource_info->debug != MagickFalse)
14632
    {
14633
      (void) LogMagickEvent(X11Event,GetMagickModule(),
14634
        "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14635
        (double) display_image->scene,(double) display_image->columns,
14636
        (double) display_image->rows);
14637
      if (display_image->colors != 0)
14638
        (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14639
          display_image->colors);
14640
      (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14641
        display_image->magick);
14642
    }
14643
  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14644
    map_info,pixel,exception);
14645
  display_image->taint=MagickFalse;
14646
  /*
14647
    Initialize graphic context.
14648
  */
14649
  windows->context.id=(Window) NULL;
14650
  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14651
    resource_info,&windows->context);
14652
  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14653
  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14654
  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14655
    class_hints->res_class[0]);
14656
  manager_hints->flags=InputHint | StateHint;
14657
  manager_hints->input=MagickFalse;
14658
  manager_hints->initial_state=WithdrawnState;
14659
  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14660
    &windows->context);
14661
  if (resource_info->debug != MagickFalse)
14662
    (void) LogMagickEvent(X11Event,GetMagickModule(),
14663
      "Window id: 0x%lx (context)",windows->context.id);
14664
  context_values.background=pixel->background_color.pixel;
14665
  context_values.font=font_info->fid;
14666
  context_values.foreground=pixel->foreground_color.pixel;
14667
  context_values.graphics_exposures=MagickFalse;
14668
  context_mask=(MagickStatusType)
14669
    (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14670
  if (pixel->annotate_context != (GC) NULL)
14671
    (void) XFreeGC(display,pixel->annotate_context);
14672
  pixel->annotate_context=XCreateGC(display,windows->context.id,
14673
    context_mask,&context_values);
14674
  if (pixel->annotate_context == (GC) NULL)
14675
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14676
      display_image->filename);
14677
  context_values.background=pixel->depth_color.pixel;
14678
  if (pixel->widget_context != (GC) NULL)
14679
    (void) XFreeGC(display,pixel->widget_context);
14680
  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14681
    &context_values);
14682
  if (pixel->widget_context == (GC) NULL)
14683
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14684
      display_image->filename);
14685
  context_values.background=pixel->foreground_color.pixel;
14686
  context_values.foreground=pixel->background_color.pixel;
14687
  context_values.plane_mask=context_values.background ^
14688
    context_values.foreground;
14689
  if (pixel->highlight_context != (GC) NULL)
14690
    (void) XFreeGC(display,pixel->highlight_context);
14691
  pixel->highlight_context=XCreateGC(display,windows->context.id,
14692
    (size_t) (context_mask | GCPlaneMask),&context_values);
14693
  if (pixel->highlight_context == (GC) NULL)
14694
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14695
      display_image->filename);
14696
  (void) XDestroyWindow(display,windows->context.id);
14697
  /*
14698
    Initialize icon window.
14699
  */
14700
  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14701
    icon_resources,&windows->icon);
14702
  windows->icon.geometry=resource_info->icon_geometry;
14703
  XBestIconSize(display,&windows->icon,display_image);
14704
  windows->icon.attributes.colormap=XDefaultColormap(display,
14705
    icon_visual->screen);
14706
  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14707
  manager_hints->flags=InputHint | StateHint;
14708
  manager_hints->input=MagickFalse;
14709
  manager_hints->initial_state=IconicState;
14710
  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14711
    &windows->icon);
14712
  if (resource_info->debug != MagickFalse)
14713
    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14714
      windows->icon.id);
14715
  /*
14716
    Initialize graphic context for icon window.
14717
  */
14718
  if (icon_pixel->annotate_context != (GC) NULL)
14719
    (void) XFreeGC(display,icon_pixel->annotate_context);
14720
  context_values.background=icon_pixel->background_color.pixel;
14721
  context_values.foreground=icon_pixel->foreground_color.pixel;
14722
  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14723
    (size_t) (GCBackground | GCForeground),&context_values);
14724
  if (icon_pixel->annotate_context == (GC) NULL)
14725
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14726
      display_image->filename);
14727
  windows->icon.annotate_context=icon_pixel->annotate_context;
14728
  /*
14729
    Initialize Image window.
14730
  */
14731
  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14732
    &windows->image);
14733
  windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14734
  if (resource_info->use_shared_memory == MagickFalse)
14735
    windows->image.shared_memory=MagickFalse;
14736
  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14737
    {
14738
      char
14739
        *title;
14740
14741
      title=InterpretImageProperties(resource_info->image_info,display_image,
14742
        resource_info->title,exception);
14743
      (void) CloneString(&windows->image.name,title);
14744
      (void) CloneString(&windows->image.icon_name,title);
14745
      title=DestroyString(title);
14746
    }
14747
  else
14748
    {
14749
      char
14750
        filename[MagickPathExtent],
14751
        window_name[MagickPathExtent];
14752
14753
      /*
14754
        Window name is the base of the filename.
14755
      */
14756
      GetPathComponent(display_image->magick_filename,TailPath,filename);
14757
      if (display_image->scene == 0)
14758
        (void) FormatLocaleString(window_name,MagickPathExtent,"%s: %s",
14759
          MagickPackageName,filename);
14760
      else
14761
        (void) FormatLocaleString(window_name,MagickPathExtent,
14762
          "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14763
          (double) display_image->scene,(double) GetImageListLength(
14764
          display_image));
14765
      (void) CloneString(&windows->image.name,window_name);
14766
      (void) CloneString(&windows->image.icon_name,filename);
14767
    }
14768
  if (resource_info->immutable)
14769
    windows->image.immutable=MagickTrue;
14770
  windows->image.use_pixmap=resource_info->use_pixmap;
14771
  windows->image.geometry=resource_info->image_geometry;
14772
  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14773
    XDisplayWidth(display,visual_info->screen),
14774
    XDisplayHeight(display,visual_info->screen));
14775
  geometry_info.width=display_image->columns;
14776
  geometry_info.height=display_image->rows;
14777
  geometry_info.x=0;
14778
  geometry_info.y=0;
14779
  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14780
    &geometry_info.width,&geometry_info.height);
14781
  windows->image.width=(unsigned int) geometry_info.width;
14782
  windows->image.height=(unsigned int) geometry_info.height;
14783
  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14784
    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14785
    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14786
    PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14787
  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14788
    resource_info,&windows->backdrop);
14789
  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14790
    {
14791
      /*
14792
        Initialize backdrop window.
14793
      */
14794
      windows->backdrop.x=0;
14795
      windows->backdrop.y=0;
14796
      (void) CloneString(&windows->backdrop.name,"Backdrop");
14797
      windows->backdrop.flags=(size_t) (USSize | USPosition);
14798
      windows->backdrop.width=(unsigned int)
14799
        XDisplayWidth(display,visual_info->screen);
14800
      windows->backdrop.height=(unsigned int)
14801
        XDisplayHeight(display,visual_info->screen);
14802
      windows->backdrop.border_width=0;
14803
      windows->backdrop.immutable=MagickTrue;
14804
      windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14805
        ButtonReleaseMask;
14806
      windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14807
        StructureNotifyMask;
14808
      manager_hints->flags=IconWindowHint | InputHint | StateHint;
14809
      manager_hints->icon_window=windows->icon.id;
14810
      manager_hints->input=MagickTrue;
14811
      manager_hints->initial_state=resource_info->iconic ? IconicState :
14812
        NormalState;
14813
      XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14814
        &windows->backdrop);
14815
      if (resource_info->debug != MagickFalse)
14816
        (void) LogMagickEvent(X11Event,GetMagickModule(),
14817
          "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14818
      (void) XMapWindow(display,windows->backdrop.id);
14819
      (void) XClearWindow(display,windows->backdrop.id);
14820
      if (windows->image.id != (Window) NULL)
14821
        {
14822
          (void) XDestroyWindow(display,windows->image.id);
14823
          windows->image.id=(Window) NULL;
14824
        }
14825
      /*
14826
        Position image in the center the backdrop.
14827
      */
14828
      windows->image.flags|=USPosition;
14829
      windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14830
        ((int) windows->image.width/2);
14831
      windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14832
        ((int) windows->image.height/2);
14833
    }
14834
  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14835
  manager_hints->icon_window=windows->icon.id;
14836
  manager_hints->input=MagickTrue;
14837
  manager_hints->initial_state=resource_info->iconic ? IconicState :
14838
    NormalState;
14839
  if (windows->group_leader.id != (Window) NULL)
14840
    {
14841
      /*
14842
        Follow the leader.
14843
      */
14844
      manager_hints->flags|=WindowGroupHint;
14845
      manager_hints->window_group=windows->group_leader.id;
14846
      (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14847
      if (resource_info->debug != MagickFalse)
14848
        (void) LogMagickEvent(X11Event,GetMagickModule(),
14849
          "Window id: 0x%lx (group leader)",windows->group_leader.id);
14850
    }
14851
  XMakeWindow(display,
14852
    (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14853
    argv,argc,class_hints,manager_hints,&windows->image);
14854
  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14855
    XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14856
  if (windows->group_leader.id != (Window) NULL)
14857
    (void) XSetTransientForHint(display,windows->image.id,
14858
      windows->group_leader.id);
14859
  if (resource_info->debug != MagickFalse)
14860
    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14861
      windows->image.id);
14862
  /*
14863
    Initialize Info widget.
14864
  */
14865
  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14866
    &windows->info);
14867
  (void) CloneString(&windows->info.name,"Info");
14868
  (void) CloneString(&windows->info.icon_name,"Info");
14869
  windows->info.border_width=1;
14870
  windows->info.x=2;
14871
  windows->info.y=2;
14872
  windows->info.flags|=PPosition;
14873
  windows->info.attributes.win_gravity=UnmapGravity;
14874
  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14875
    StructureNotifyMask;
14876
  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14877
  manager_hints->input=MagickFalse;
14878
  manager_hints->initial_state=NormalState;
14879
  manager_hints->window_group=windows->image.id;
14880
  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14881
    &windows->info);
14882
  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14883
    windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14884
  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14885
    windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14886
  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14887
  if (windows->image.mapped != MagickFalse)
14888
    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14889
  if (resource_info->debug != MagickFalse)
14890
    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14891
      windows->info.id);
14892
  /*
14893
    Initialize Command widget.
14894
  */
14895
  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14896
    resource_info,&windows->command);
14897
  windows->command.data=MagickMenus;
14898
  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14899
  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14900
    resource_info->client_name);
14901
  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14902
    resource_name,"geometry",(char *) NULL);
14903
  (void) CloneString(&windows->command.name,MagickTitle);
14904
  windows->command.border_width=0;
14905
  windows->command.flags|=PPosition;
14906
  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14907
    ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14908
    OwnerGrabButtonMask | StructureNotifyMask;
14909
  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14910
  manager_hints->input=MagickTrue;
14911
  manager_hints->initial_state=NormalState;
14912
  manager_hints->window_group=windows->image.id;
14913
  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14914
    &windows->command);
14915
  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14916
    windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14917
    HighlightHeight);
14918
  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14919
    windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14920
  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14921
  if (windows->command.mapped != MagickFalse)
14922
    (void) XMapRaised(display,windows->command.id);
14923
  if (resource_info->debug != MagickFalse)
14924
    (void) LogMagickEvent(X11Event,GetMagickModule(),
14925
      "Window id: 0x%lx (command)",windows->command.id);
14926
  /*
14927
    Initialize Widget window.
14928
  */
14929
  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14930
    resource_info,&windows->widget);
14931
  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14932
    resource_info->client_name);
14933
  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14934
    resource_name,"geometry",(char *) NULL);
14935
  windows->widget.border_width=0;
14936
  windows->widget.flags|=PPosition;
14937
  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14938
    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14939
    KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14940
    StructureNotifyMask;
14941
  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14942
  manager_hints->input=MagickTrue;
14943
  manager_hints->initial_state=NormalState;
14944
  manager_hints->window_group=windows->image.id;
14945
  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14946
    &windows->widget);
14947
  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14948
    windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14949
  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14950
    windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14951
  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14952
  if (resource_info->debug != MagickFalse)
14953
    (void) LogMagickEvent(X11Event,GetMagickModule(),
14954
      "Window id: 0x%lx (widget)",windows->widget.id);
14955
  /*
14956
    Initialize popup window.
14957
  */
14958
  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14959
    resource_info,&windows->popup);
14960
  windows->popup.border_width=0;
14961
  windows->popup.flags|=PPosition;
14962
  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14963
    ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14964
    KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14965
  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14966
  manager_hints->input=MagickTrue;
14967
  manager_hints->initial_state=NormalState;
14968
  manager_hints->window_group=windows->image.id;
14969
  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14970
    &windows->popup);
14971
  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14972
    windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14973
  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14974
    windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14975
  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14976
  if (resource_info->debug != MagickFalse)
14977
    (void) LogMagickEvent(X11Event,GetMagickModule(),
14978
      "Window id: 0x%lx (pop up)",windows->popup.id);
14979
  /*
14980
    Initialize Magnify window and cursor.
14981
  */
14982
  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14983
    resource_info,&windows->magnify);
14984
  if (resource_info->use_shared_memory == MagickFalse)
14985
    windows->magnify.shared_memory=MagickFalse;
14986
  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14987
    resource_info->client_name);
14988
  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14989
    resource_name,"geometry",(char *) NULL);
14990
  (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,
14991
    "Magnify %uX",resource_info->magnify);
14992
  if (windows->magnify.cursor != (Cursor) NULL)
14993
    (void) XFreeCursor(display,windows->magnify.cursor);
14994
  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14995
    map_info->colormap,resource_info->background_color,
14996
    resource_info->foreground_color);
14997
  if (windows->magnify.cursor == (Cursor) NULL)
14998
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14999
      display_image->filename);
15000
  windows->magnify.width=MagnifySize;
15001
  windows->magnify.height=MagnifySize;
15002
  windows->magnify.flags|=PPosition;
15003
  windows->magnify.min_width=MagnifySize;
15004
  windows->magnify.min_height=MagnifySize;
15005
  windows->magnify.width_inc=MagnifySize;
15006
  windows->magnify.height_inc=MagnifySize;
15007
  windows->magnify.data=resource_info->magnify;
15008
  windows->magnify.attributes.cursor=windows->magnify.cursor;
15009
  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
15010
    ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
15011
    StructureNotifyMask;
15012
  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15013
  manager_hints->input=MagickTrue;
15014
  manager_hints->initial_state=NormalState;
15015
  manager_hints->window_group=windows->image.id;
15016
  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15017
    &windows->magnify);
15018
  if (resource_info->debug != MagickFalse)
15019
    (void) LogMagickEvent(X11Event,GetMagickModule(),
15020
      "Window id: 0x%lx (magnify)",windows->magnify.id);
15021
  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15022
  /*
15023
    Initialize panning window.
15024
  */
15025
  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15026
    resource_info,&windows->pan);
15027
  (void) CloneString(&windows->pan.name,"Pan Icon");
15028
  windows->pan.width=windows->icon.width;
15029
  windows->pan.height=windows->icon.height;
15030
  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15031
    resource_info->client_name);
15032
  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15033
    resource_name,"geometry",(char *) NULL);
15034
  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15035
    &windows->pan.width,&windows->pan.height);
15036
  windows->pan.flags|=PPosition;
15037
  windows->pan.immutable=MagickTrue;
15038
  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15039
    ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15040
    StructureNotifyMask;
15041
  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15042
  manager_hints->input=MagickFalse;
15043
  manager_hints->initial_state=NormalState;
15044
  manager_hints->window_group=windows->image.id;
15045
  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15046
    &windows->pan);
15047
  if (resource_info->debug != MagickFalse)
15048
    (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15049
      windows->pan.id);
15050
  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15051
  if (windows->info.mapped != MagickFalse)
15052
    (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15053
  if ((windows->image.mapped == MagickFalse) ||
15054
      (windows->backdrop.id != (Window) NULL))
15055
    (void) XMapWindow(display,windows->image.id);
15056
  /*
15057
    Set our progress monitor and warning handlers.
15058
  */
15059
  if (warning_handler == (WarningHandler) NULL)
15060
    {
15061
      warning_handler=resource_info->display_warnings ?
15062
        SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15063
      warning_handler=resource_info->display_warnings ?
15064
        SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15065
    }
15066
  /*
15067
    Initialize Image and Magnify X images.
15068
  */
15069
  windows->image.x=0;
15070
  windows->image.y=0;
15071
  windows->magnify.shape=MagickFalse;
15072
  width=(unsigned int) display_image->columns;
15073
  height=(unsigned int) display_image->rows;
15074
  if ((display_image->columns != width) || (display_image->rows != height))
15075
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15076
      display_image->filename);
15077
  status=XMakeImage(display,resource_info,&windows->image,display_image,
15078
    width,height,exception);
15079
  if (status == MagickFalse)
15080
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15081
      display_image->filename);
15082
  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15083
    windows->magnify.width,windows->magnify.height,exception);
15084
  if (status == MagickFalse)
15085
    ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15086
      display_image->filename);
15087
  if (windows->magnify.mapped != MagickFalse)
15088
    (void) XMapRaised(display,windows->magnify.id);
15089
  if (windows->pan.mapped != MagickFalse)
15090
    (void) XMapRaised(display,windows->pan.id);
15091
  windows->image.window_changes.width=(int) display_image->columns;
15092
  windows->image.window_changes.height=(int) display_image->rows;
15093
  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15094
  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15095
  (void) XSync(display,MagickFalse);
15096
  /*
15097
    Respond to events.
15098
  */
15099
  delay=display_image->delay/(size_t)
15100
    MagickMax(display_image->ticks_per_second,1L);
15101
  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15102
  update_time=0;
15103
  if (resource_info->update != MagickFalse)
15104
    {
15105
      MagickBooleanType
15106
        status;
15107
15108
      /*
15109
        Determine when file data was last modified.
15110
      */
15111
      status=GetPathAttributes(display_image->filename,&attributes);
15112
      if (status != MagickFalse)
15113
        update_time=attributes.st_mtime;
15114
    }
15115
  *state&=(unsigned int) (~FormerImageState);
15116
  *state&=(unsigned int) (~MontageImageState);
15117
  *state&=(unsigned int) (~NextImageState);
15118
  do
15119
  {
15120
    /*
15121
      Handle a window event.
15122
    */
15123
    if (windows->image.mapped != MagickFalse)
15124
      if ((display_image->delay != 0) || (resource_info->update != 0))
15125
        {
15126
          if (timer < GetMagickTime())
15127
            {
15128
              if (resource_info->update == MagickFalse)
15129
                *state|=NextImageState | ExitState;
15130
              else
15131
                {
15132
                  MagickBooleanType
15133
                    status;
15134
15135
                  /*
15136
                    Determine if image file was modified.
15137
                  */
15138
                  status=GetPathAttributes(display_image->filename,&attributes);
15139
                  if (status != MagickFalse)
15140
                    if (update_time != attributes.st_mtime)
15141
                      {
15142
                        /*
15143
                          Redisplay image.
15144
                        */
15145
                        (void) FormatLocaleString(
15146
                          resource_info->image_info->filename,MagickPathExtent,
15147
                          "%s:%s",display_image->magick,
15148
                          display_image->filename);
15149
                        nexus=ReadImage(resource_info->image_info,exception);
15150
                        if (nexus != (Image *) NULL)
15151
                          *state|=NextImageState | ExitState;
15152
                      }
15153
                  delay=display_image->delay/(size_t) MagickMax(
15154
                    display_image->ticks_per_second,1L);
15155
                  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15156
                }
15157
            }
15158
          if (XEventsQueued(display,QueuedAfterFlush) == 0)
15159
            {
15160
              /*
15161
                Do not block if delay > 0.
15162
              */
15163
              XDelay(display,SuspendTime << 2);
15164
              continue;
15165
            }
15166
        }
15167
    timestamp=GetMagickTime();
15168
    (void) XNextEvent(display,&event);
15169
    if ((windows->image.stasis == MagickFalse) ||
15170
        (windows->magnify.stasis == MagickFalse))
15171
      {
15172
        if ((GetMagickTime()-timestamp) > 0)
15173
          {
15174
            windows->image.stasis=MagickTrue;
15175
            windows->magnify.stasis=MagickTrue;
15176
          }
15177
      }
15178
    if (event.xany.window == windows->command.id)
15179
      {
15180
        /*
15181
          Select a command from the Command widget.
15182
        */
15183
        id=XCommandWidget(display,windows,CommandMenu,&event);
15184
        if (id < 0)
15185
          continue;
15186
        (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15187
        display_command=CommandMenus[id];
15188
        if (id < MagickMenus)
15189
          {
15190
            /*
15191
              Select a command from a pop-up menu.
15192
            */
15193
            entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15194
              command);
15195
            if (entry < 0)
15196
              continue;
15197
            (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15198
            display_command=Commands[id][entry];
15199
          }
15200
        if (display_command != NullCommand)
15201
          nexus=XMagickCommand(display,resource_info,windows,display_command,
15202
            &display_image,exception);
15203
        continue;
15204
      }
15205
    switch (event.type)
15206
    {
15207
      case ButtonPress:
15208
      {
15209
        if (resource_info->debug != MagickFalse)
15210
          (void) LogMagickEvent(X11Event,GetMagickModule(),
15211
            "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15212
            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15213
        if ((event.xbutton.button == Button3) &&
15214
            (event.xbutton.state & Mod1Mask))
15215
          {
15216
            /*
15217
              Convert Alt-Button3 to Button2.
15218
            */
15219
            event.xbutton.button=Button2;
15220
            event.xbutton.state&=(unsigned int) (~Mod1Mask);
15221
          }
15222
        if (event.xbutton.window == windows->backdrop.id)
15223
          {
15224
            (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15225
              event.xbutton.time);
15226
            break;
15227
          }
15228
        if (event.xbutton.window == windows->image.id)
15229
          {
15230
            switch (event.xbutton.button)
15231
            {
15232
              case Button1:
15233
              {
15234
                if (resource_info->immutable)
15235
                  {
15236
                    /*
15237
                      Select a command from the Virtual menu.
15238
                    */
15239
                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15240
                      command);
15241
                    if (entry >= 0)
15242
                      nexus=XMagickCommand(display,resource_info,windows,
15243
                        VirtualCommands[entry],&display_image,exception);
15244
                    break;
15245
                  }
15246
                /*
15247
                  Map/unmap Command widget.
15248
                */
15249
                if (windows->command.mapped != MagickFalse)
15250
                  (void) XWithdrawWindow(display,windows->command.id,
15251
                    windows->command.screen);
15252
                else
15253
                  {
15254
                    (void) XCommandWidget(display,windows,CommandMenu,
15255
                      (XEvent *) NULL);
15256
                    (void) XMapRaised(display,windows->command.id);
15257
                  }
15258
                break;
15259
              }
15260
              case Button2:
15261
              {
15262
                /*
15263
                  User pressed the image magnify button.
15264
                */
15265
                (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15266
                  &display_image,exception);
15267
                XMagnifyImage(display,windows,&event,exception);
15268
                break;
15269
              }
15270
              case Button3:
15271
              {
15272
                if (resource_info->immutable)
15273
                  {
15274
                    /*
15275
                      Select a command from the Virtual menu.
15276
                    */
15277
                    entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15278
                      command);
15279
                    if (entry >= 0)
15280
                      nexus=XMagickCommand(display,resource_info,windows,
15281
                        VirtualCommands[entry],&display_image,exception);
15282
                    break;
15283
                  }
15284
                if (display_image->montage != (char *) NULL)
15285
                  {
15286
                    /*
15287
                      Open or delete a tile from a visual image directory.
15288
                    */
15289
                    nexus=XTileImage(display,resource_info,windows,
15290
                      display_image,&event,exception);
15291
                    if (nexus != (Image *) NULL)
15292
                      *state|=MontageImageState | NextImageState | ExitState;
15293
                    vid_info.x=(short int) windows->image.x;
15294
                    vid_info.y=(short int) windows->image.y;
15295
                    break;
15296
                  }
15297
                /*
15298
                  Select a command from the Short Cuts menu.
15299
                */
15300
                entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15301
                  command);
15302
                if (entry >= 0)
15303
                  nexus=XMagickCommand(display,resource_info,windows,
15304
                    ShortCutsCommands[entry],&display_image,exception);
15305
                break;
15306
              }
15307
              case Button4:
15308
              {
15309
                /*
15310
                  Wheel up.
15311
                */
15312
                XTranslateImage(display,windows,*image,XK_Up);
15313
                break;
15314
              }
15315
              case Button5:
15316
              {
15317
                /*
15318
                  Wheel down.
15319
                */
15320
                XTranslateImage(display,windows,*image,XK_Down);
15321
                break;
15322
              }
15323
              default:
15324
                break;
15325
            }
15326
            break;
15327
          }
15328
        if (event.xbutton.window == windows->magnify.id)
15329
          {
15330
            const char
15331
              *const MagnifyMenu[] =
15332
              {
15333
                "2",
15334
                "4",
15335
                "5",
15336
                "6",
15337
                "7",
15338
                "8",
15339
                "9",
15340
                "3",
15341
                (char *) NULL,
15342
              };
15343
15344
            int
15345
              factor;
15346
15347
            static KeySym
15348
              MagnifyCommands[] =
15349
              {
15350
                XK_2,
15351
                XK_4,
15352
                XK_5,
15353
                XK_6,
15354
                XK_7,
15355
                XK_8,
15356
                XK_9,
15357
                XK_3
15358
              };
15359
15360
            /*
15361
              Select a magnify factor from the pop-up menu.
15362
            */
15363
            factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15364
            if (factor >= 0)
15365
              XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15366
                exception);
15367
            break;
15368
          }
15369
        if (event.xbutton.window == windows->pan.id)
15370
          {
15371
            switch (event.xbutton.button)
15372
            {
15373
              case Button4:
15374
              {
15375
                /*
15376
                  Wheel up.
15377
                */
15378
                XTranslateImage(display,windows,*image,XK_Up);
15379
                break;
15380
              }
15381
              case Button5:
15382
              {
15383
                /*
15384
                  Wheel down.
15385
                */
15386
                XTranslateImage(display,windows,*image,XK_Down);
15387
                break;
15388
              }
15389
              default:
15390
              {
15391
                XPanImage(display,windows,&event,exception);
15392
                break;
15393
              }
15394
            }
15395
            break;
15396
          }
15397
        delay=display_image->delay/(size_t)
15398
          MagickMax(display_image->ticks_per_second,1L);
15399
        timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15400
        break;
15401
      }
15402
      case ButtonRelease:
15403
      {
15404
        if (resource_info->debug != MagickFalse)
15405
          (void) LogMagickEvent(X11Event,GetMagickModule(),
15406
            "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15407
            event.xbutton.button,event.xbutton.x,event.xbutton.y);
15408
        break;
15409
      }
15410
      case ClientMessage:
15411
      {
15412
        if (resource_info->debug != MagickFalse)
15413
          (void) LogMagickEvent(X11Event,GetMagickModule(),
15414
            "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15415
            event.xclient.message_type,event.xclient.format,(unsigned long)
15416
            event.xclient.data.l[0]);
15417
        if (event.xclient.message_type == windows->im_protocols)
15418
          {
15419
            if (*event.xclient.data.l == (long) windows->im_update_widget)
15420
              {
15421
                (void) CloneString(&windows->command.name,MagickTitle);
15422
                windows->command.data=MagickMenus;
15423
                (void) XCommandWidget(display,windows,CommandMenu,
15424
                  (XEvent *) NULL);
15425
                break;
15426
              }
15427
            if (*event.xclient.data.l == (long) windows->im_update_colormap)
15428
              {
15429
                /*
15430
                  Update graphic context and window colormap.
15431
                */
15432
                for (i=0; i < (int) number_windows; i++)
15433
                {
15434
                  if (magick_windows[i]->id == windows->icon.id)
15435
                    continue;
15436
                  context_values.background=pixel->background_color.pixel;
15437
                  context_values.foreground=pixel->foreground_color.pixel;
15438
                  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15439
                    context_mask,&context_values);
15440
                  (void) XChangeGC(display,magick_windows[i]->widget_context,
15441
                    context_mask,&context_values);
15442
                  context_values.background=pixel->foreground_color.pixel;
15443
                  context_values.foreground=pixel->background_color.pixel;
15444
                  context_values.plane_mask=context_values.background ^
15445
                    context_values.foreground;
15446
                  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15447
                    (size_t) (context_mask | GCPlaneMask),
15448
                    &context_values);
15449
                  magick_windows[i]->attributes.background_pixel=
15450
                    pixel->background_color.pixel;
15451
                  magick_windows[i]->attributes.border_pixel=
15452
                    pixel->border_color.pixel;
15453
                  magick_windows[i]->attributes.colormap=map_info->colormap;
15454
                  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15455
                    (unsigned long) magick_windows[i]->mask,
15456
                    &magick_windows[i]->attributes);
15457
                }
15458
                if (windows->pan.mapped != MagickFalse)
15459
                  {
15460
                    (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15461
                      windows->pan.pixmap);
15462
                    (void) XClearWindow(display,windows->pan.id);
15463
                    XDrawPanRectangle(display,windows);
15464
                  }
15465
                if (windows->backdrop.id != (Window) NULL)
15466
                  (void) XInstallColormap(display,map_info->colormap);
15467
                break;
15468
              }
15469
            if (*event.xclient.data.l == (long) windows->im_former_image)
15470
              {
15471
                *state|=FormerImageState | ExitState;
15472
                break;
15473
              }
15474
            if (*event.xclient.data.l == (long) windows->im_next_image)
15475
              {
15476
                *state|=NextImageState | ExitState;
15477
                break;
15478
              }
15479
            if (*event.xclient.data.l == (long) windows->im_retain_colors)
15480
              {
15481
                *state|=RetainColorsState;
15482
                break;
15483
              }
15484
            if (*event.xclient.data.l == (long) windows->im_exit)
15485
              {
15486
                *state|=ExitState;
15487
                break;
15488
              }
15489
            break;
15490
          }
15491
        if (event.xclient.message_type == windows->dnd_protocols)
15492
          {
15493
            Atom
15494
              selection,
15495
              type;
15496
15497
            int
15498
              format,
15499
              status;
15500
15501
            unsigned char
15502
              *data;
15503
15504
            unsigned long
15505
              after,
15506
              length;
15507
15508
            /*
15509
              Display image named by the Drag-and-Drop selection.
15510
            */
15511
            if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15512
              break;
15513
            selection=XInternAtom(display,"DndSelection",MagickFalse);
15514
            status=XGetWindowProperty(display,root_window,selection,0L,(long)
15515
              MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15516
              &length,&after,&data);
15517
            if ((status != Success) || (length == 0))
15518
              break;
15519
            if (*event.xclient.data.l == 2)
15520
              {
15521
                /*
15522
                  Offix DND.
15523
                */
15524
                (void) CopyMagickString(resource_info->image_info->filename,
15525
                  (char *) data,MagickPathExtent);
15526
              }
15527
            else
15528
              {
15529
                /*
15530
                  XDND.
15531
                */
15532
                if (strncmp((char *) data, "file:", 5) != 0)
15533
                  {
15534
                    (void) XFree((void *) data);
15535
                    break;
15536
                  }
15537
                (void) CopyMagickString(resource_info->image_info->filename,
15538
                  ((char *) data)+5,MagickPathExtent);
15539
              }
15540
            nexus=ReadImage(resource_info->image_info,exception);
15541
            CatchException(exception);
15542
            if (nexus != (Image *) NULL)
15543
              *state|=NextImageState | ExitState;
15544
            (void) XFree((void *) data);
15545
            break;
15546
          }
15547
        /*
15548
          If client window delete message, exit.
15549
        */
15550
        if (event.xclient.message_type != windows->wm_protocols)
15551
          break;
15552
        if (*event.xclient.data.l != (long) windows->wm_delete_window)
15553
          break;
15554
        (void) XWithdrawWindow(display,event.xclient.window,
15555
          visual_info->screen);
15556
        if (event.xclient.window == windows->image.id)
15557
          {
15558
            *state|=ExitState;
15559
            break;
15560
          }
15561
        if (event.xclient.window == windows->pan.id)
15562
          {
15563
            /*
15564
              Restore original image size when pan window is deleted.
15565
            */
15566
            windows->image.window_changes.width=windows->image.ximage->width;
15567
            windows->image.window_changes.height=windows->image.ximage->height;
15568
            (void) XConfigureImage(display,resource_info,windows,
15569
              display_image,exception);
15570
          }
15571
        break;
15572
      }
15573
      case ConfigureNotify:
15574
      {
15575
        if (resource_info->debug != MagickFalse)
15576
          (void) LogMagickEvent(X11Event,GetMagickModule(),
15577
            "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15578
            event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15579
            event.xconfigure.y,event.xconfigure.send_event);
15580
        if (event.xconfigure.window == windows->image.id)
15581
          {
15582
            /*
15583
              Image window has a new configuration.
15584
            */
15585
            if (event.xconfigure.send_event != 0)
15586
              {
15587
                XWindowChanges
15588
                  window_changes;
15589
15590
                /*
15591
                  Position the transient windows relative of the Image window.
15592
                */
15593
                if (windows->command.geometry == (char *) NULL)
15594
                  if (windows->command.mapped == MagickFalse)
15595
                    {
15596
                      windows->command.x=event.xconfigure.x-(int)
15597
                        windows->command.width-25;
15598
                      windows->command.y=event.xconfigure.y;
15599
                      XConstrainWindowPosition(display,&windows->command);
15600
                      window_changes.x=windows->command.x;
15601
                      window_changes.y=windows->command.y;
15602
                      (void) XReconfigureWMWindow(display,windows->command.id,
15603
                        windows->command.screen,(unsigned int) (CWX | CWY),
15604
                        &window_changes);
15605
                    }
15606
                if (windows->widget.geometry == (char *) NULL)
15607
                  if (windows->widget.mapped == MagickFalse)
15608
                    {
15609
                      windows->widget.x=event.xconfigure.x+
15610
                        event.xconfigure.width/10;
15611
                      windows->widget.y=event.xconfigure.y+
15612
                        event.xconfigure.height/10;
15613
                      XConstrainWindowPosition(display,&windows->widget);
15614
                      window_changes.x=windows->widget.x;
15615
                      window_changes.y=windows->widget.y;
15616
                      (void) XReconfigureWMWindow(display,windows->widget.id,
15617
                        windows->widget.screen,(unsigned int) (CWX | CWY),
15618
                        &window_changes);
15619
                    }
15620
                if (windows->magnify.geometry == (char *) NULL)
15621
                  if (windows->magnify.mapped == MagickFalse)
15622
                    {
15623
                      windows->magnify.x=event.xconfigure.x+
15624
                        event.xconfigure.width+25;
15625
                      windows->magnify.y=event.xconfigure.y;
15626
                      XConstrainWindowPosition(display,&windows->magnify);
15627
                      window_changes.x=windows->magnify.x;
15628
                      window_changes.y=windows->magnify.y;
15629
                      (void) XReconfigureWMWindow(display,windows->magnify.id,
15630
                        windows->magnify.screen,(unsigned int) (CWX | CWY),
15631
                        &window_changes);
15632
                    }
15633
                if (windows->pan.geometry == (char *) NULL)
15634
                  if (windows->pan.mapped == MagickFalse)
15635
                    {
15636
                      windows->pan.x=event.xconfigure.x+(int)
15637
                        event.xconfigure.width+25;
15638
                      windows->pan.y=event.xconfigure.y+(int)
15639
                        windows->magnify.height+50;
15640
                      XConstrainWindowPosition(display,&windows->pan);
15641
                      window_changes.x=windows->pan.x;
15642
                      window_changes.y=windows->pan.y;
15643
                      (void) XReconfigureWMWindow(display,windows->pan.id,
15644
                        windows->pan.screen,(unsigned int) (CWX | CWY),
15645
                        &window_changes);
15646
                    }
15647
              }
15648
            if ((event.xconfigure.width == (int) windows->image.width) &&
15649
                (event.xconfigure.height == (int) windows->image.height))
15650
              break;
15651
            windows->image.width=(unsigned int) event.xconfigure.width;
15652
            windows->image.height=(unsigned int) event.xconfigure.height;
15653
            windows->image.x=0;
15654
            windows->image.y=0;
15655
            if (display_image->montage != (char *) NULL)
15656
              {
15657
                windows->image.x=vid_info.x;
15658
                windows->image.y=vid_info.y;
15659
              }
15660
            if (windows->image.mapped != MagickFalse &&
15661
                windows->image.stasis != MagickFalse)
15662
              {
15663
                /*
15664
                  Update image window configuration.
15665
                */
15666
                windows->image.window_changes.width=event.xconfigure.width;
15667
                windows->image.window_changes.height=event.xconfigure.height;
15668
                (void) XConfigureImage(display,resource_info,windows,
15669
                  display_image,exception);
15670
              }
15671
            /*
15672
              Update pan window configuration.
15673
            */
15674
            if ((event.xconfigure.width < windows->image.ximage->width) ||
15675
                (event.xconfigure.height < windows->image.ximage->height))
15676
              {
15677
                (void) XMapRaised(display,windows->pan.id);
15678
                XDrawPanRectangle(display,windows);
15679
              }
15680
            else
15681
              if (windows->pan.mapped != MagickFalse)
15682
                (void) XWithdrawWindow(display,windows->pan.id,
15683
                  windows->pan.screen);
15684
            break;
15685
          }
15686
        if (event.xconfigure.window == windows->magnify.id)
15687
          {
15688
            unsigned int
15689
              magnify;
15690
15691
            /*
15692
              Magnify window has a new configuration.
15693
            */
15694
            windows->magnify.width=(unsigned int) event.xconfigure.width;
15695
            windows->magnify.height=(unsigned int) event.xconfigure.height;
15696
            if (windows->magnify.mapped == MagickFalse)
15697
              break;
15698
            magnify=1;
15699
            while ((int) magnify <= event.xconfigure.width)
15700
              magnify<<=1;
15701
            while ((int) magnify <= event.xconfigure.height)
15702
              magnify<<=1;
15703
            magnify>>=1;
15704
            if (((int) magnify != event.xconfigure.width) ||
15705
                ((int) magnify != event.xconfigure.height))
15706
              {
15707
                window_changes.width=(int) magnify;
15708
                window_changes.height=(int) magnify;
15709
                (void) XReconfigureWMWindow(display,windows->magnify.id,
15710
                  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15711
                  &window_changes);
15712
                break;
15713
              }
15714
            if (windows->magnify.mapped != MagickFalse &&
15715
                windows->magnify.stasis != MagickFalse)
15716
              {
15717
                status=XMakeImage(display,resource_info,&windows->magnify,
15718
                  display_image,windows->magnify.width,windows->magnify.height,
15719
                  exception);
15720
                XMakeMagnifyImage(display,windows,exception);
15721
              }
15722
            break;
15723
          }
15724
        if (windows->magnify.mapped != MagickFalse &&
15725
            (event.xconfigure.window == windows->pan.id))
15726
          {
15727
            /*
15728
              Pan icon window has a new configuration.
15729
            */
15730
            if (event.xconfigure.send_event != 0)
15731
              {
15732
                windows->pan.x=event.xconfigure.x;
15733
                windows->pan.y=event.xconfigure.y;
15734
              }
15735
            windows->pan.width=(unsigned int) event.xconfigure.width;
15736
            windows->pan.height=(unsigned int) event.xconfigure.height;
15737
            break;
15738
          }
15739
        if (event.xconfigure.window == windows->icon.id)
15740
          {
15741
            /*
15742
              Icon window has a new configuration.
15743
            */
15744
            windows->icon.width=(unsigned int) event.xconfigure.width;
15745
            windows->icon.height=(unsigned int) event.xconfigure.height;
15746
            break;
15747
          }
15748
        break;
15749
      }
15750
      case DestroyNotify:
15751
      {
15752
        /*
15753
          Group leader has exited.
15754
        */
15755
        if (resource_info->debug != MagickFalse)
15756
          (void) LogMagickEvent(X11Event,GetMagickModule(),
15757
            "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15758
        if (event.xdestroywindow.window == windows->group_leader.id)
15759
          {
15760
            *state|=ExitState;
15761
            break;
15762
          }
15763
        break;
15764
      }
15765
      case EnterNotify:
15766
      {
15767
        /*
15768
          Selectively install colormap.
15769
        */
15770
        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15771
          if (event.xcrossing.mode != NotifyUngrab)
15772
            XInstallColormap(display,map_info->colormap);
15773
        break;
15774
      }
15775
      case Expose:
15776
      {
15777
        if (resource_info->debug != MagickFalse)
15778
          (void) LogMagickEvent(X11Event,GetMagickModule(),
15779
            "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15780
            event.xexpose.width,event.xexpose.height,event.xexpose.x,
15781
            event.xexpose.y);
15782
        /*
15783
          Refresh windows that are now exposed.
15784
        */
15785
        if ((event.xexpose.window == windows->image.id) &&
15786
            windows->image.mapped != MagickFalse)
15787
          {
15788
            XRefreshWindow(display,&windows->image,&event);
15789
            delay=display_image->delay/(size_t) MagickMax(
15790
              display_image->ticks_per_second,1L);
15791
            timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15792
            break;
15793
          }
15794
        if ((event.xexpose.window == windows->magnify.id) &&
15795
            windows->magnify.mapped != MagickFalse)
15796
          {
15797
            XMakeMagnifyImage(display,windows,exception);
15798
            break;
15799
          }
15800
        if (event.xexpose.window == windows->pan.id)
15801
          {
15802
            XDrawPanRectangle(display,windows);
15803
            break;
15804
          }
15805
        if (event.xexpose.window == windows->icon.id)
15806
          {
15807
            XRefreshWindow(display,&windows->icon,&event);
15808
            break;
15809
          }
15810
        break;
15811
      }
15812
      case KeyPress:
15813
      {
15814
        int
15815
          length;
15816
15817
        /*
15818
          Respond to a user key press.
15819
        */
15820
        length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15821
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15822
        *(command+length)='\0';
15823
        if (resource_info->debug != MagickFalse)
15824
          (void) LogMagickEvent(X11Event,GetMagickModule(),
15825
            "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15826
            key_symbol,command);
15827
        if (event.xkey.window == windows->image.id)
15828
          {
15829
            display_command=XImageWindowCommand(display,resource_info,windows,
15830
              event.xkey.state,key_symbol,&display_image,exception);
15831
            if (display_command != NullCommand)
15832
              nexus=XMagickCommand(display,resource_info,windows,
15833
                display_command,&display_image,exception);
15834
          }
15835
        if (event.xkey.window == windows->magnify.id)
15836
          XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15837
            exception);
15838
        if (event.xkey.window == windows->pan.id)
15839
          {
15840
            if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15841
              (void) XWithdrawWindow(display,windows->pan.id,
15842
                windows->pan.screen);
15843
            else
15844
              if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15845
                XTextViewHelp(display,resource_info,windows,MagickFalse,
15846
                  "Help Viewer - Image Pan",ImagePanHelp);
15847
              else
15848
                XTranslateImage(display,windows,*image,key_symbol);
15849
          }
15850
        delay=display_image->delay/(size_t) MagickMax(
15851
          display_image->ticks_per_second,1L);
15852
        timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15853
        break;
15854
      }
15855
      case KeyRelease:
15856
      {
15857
        /*
15858
          Respond to a user key release.
15859
        */
15860
        (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15861
          sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15862
        if (resource_info->debug != MagickFalse)
15863
          (void) LogMagickEvent(X11Event,GetMagickModule(),
15864
            "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15865
        break;
15866
      }
15867
      case LeaveNotify:
15868
      {
15869
        /*
15870
          Selectively uninstall colormap.
15871
        */
15872
        if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15873
          if (event.xcrossing.mode != NotifyUngrab)
15874
            XUninstallColormap(display,map_info->colormap);
15875
        break;
15876
      }
15877
      case MapNotify:
15878
      {
15879
        if (resource_info->debug != MagickFalse)
15880
          (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15881
            event.xmap.window);
15882
        if (event.xmap.window == windows->backdrop.id)
15883
          {
15884
            (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15885
              CurrentTime);
15886
            windows->backdrop.mapped=MagickTrue;
15887
            break;
15888
          }
15889
        if (event.xmap.window == windows->image.id)
15890
          {
15891
            if (windows->backdrop.id != (Window) NULL)
15892
              (void) XInstallColormap(display,map_info->colormap);
15893
            if (LocaleCompare(display_image->magick,"LOGO") == 0)
15894
              {
15895
                if (LocaleCompare(display_image->filename,"LOGO") == 0)
15896
                  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15897
              }
15898
            if (((int) windows->image.width < windows->image.ximage->width) ||
15899
                ((int) windows->image.height < windows->image.ximage->height))
15900
              (void) XMapRaised(display,windows->pan.id);
15901
            windows->image.mapped=MagickTrue;
15902
            break;
15903
          }
15904
        if (event.xmap.window == windows->magnify.id)
15905
          {
15906
            XMakeMagnifyImage(display,windows,exception);
15907
            windows->magnify.mapped=MagickTrue;
15908
            (void) XWithdrawWindow(display,windows->info.id,
15909
              windows->info.screen);
15910
            break;
15911
          }
15912
        if (event.xmap.window == windows->pan.id)
15913
          {
15914
            XMakePanImage(display,resource_info,windows,display_image,
15915
              exception);
15916
            windows->pan.mapped=MagickTrue;
15917
            break;
15918
          }
15919
        if (event.xmap.window == windows->info.id)
15920
          {
15921
            windows->info.mapped=MagickTrue;
15922
            break;
15923
          }
15924
        if (event.xmap.window == windows->icon.id)
15925
          {
15926
            MagickBooleanType
15927
              taint;
15928
15929
            /*
15930
              Create an icon image.
15931
            */
15932
            taint=display_image->taint;
15933
            XMakeStandardColormap(display,icon_visual,icon_resources,
15934
              display_image,icon_map,icon_pixel,exception);
15935
            (void) XMakeImage(display,icon_resources,&windows->icon,
15936
              display_image,windows->icon.width,windows->icon.height,
15937
              exception);
15938
            display_image->taint=taint;
15939
            (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15940
              windows->icon.pixmap);
15941
            (void) XClearWindow(display,windows->icon.id);
15942
            (void) XWithdrawWindow(display,windows->info.id,
15943
              windows->info.screen);
15944
            windows->icon.mapped=MagickTrue;
15945
            break;
15946
          }
15947
        if (event.xmap.window == windows->command.id)
15948
          {
15949
            windows->command.mapped=MagickTrue;
15950
            break;
15951
          }
15952
        if (event.xmap.window == windows->popup.id)
15953
          {
15954
            windows->popup.mapped=MagickTrue;
15955
            break;
15956
          }
15957
        if (event.xmap.window == windows->widget.id)
15958
          {
15959
            windows->widget.mapped=MagickTrue;
15960
            break;
15961
          }
15962
        break;
15963
      }
15964
      case MappingNotify:
15965
      {
15966
        (void) XRefreshKeyboardMapping(&event.xmapping);
15967
        break;
15968
      }
15969
      case NoExpose:
15970
        break;
15971
      case PropertyNotify:
15972
      {
15973
        Atom
15974
          type;
15975
15976
        int
15977
          format,
15978
          status;
15979
15980
        unsigned char
15981
          *data;
15982
15983
        unsigned long
15984
          after,
15985
          length;
15986
15987
        if (resource_info->debug != MagickFalse)
15988
          (void) LogMagickEvent(X11Event,GetMagickModule(),
15989
            "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15990
            event.xproperty.atom,event.xproperty.state);
15991
        if (event.xproperty.atom != windows->im_remote_command)
15992
          break;
15993
        /*
15994
          Display image named by the remote command protocol.
15995
        */
15996
        status=XGetWindowProperty(display,event.xproperty.window,
15997
          event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15998
          AnyPropertyType,&type,&format,&length,&after,&data);
15999
        if ((status != Success) || (length == 0))
16000
          break;
16001
        if (LocaleCompare((char *) data,"-quit") == 0)
16002
          {
16003
            XClientMessage(display,windows->image.id,windows->im_protocols,
16004
              windows->im_exit,CurrentTime);
16005
            (void) XFree((void *) data);
16006
            break;
16007
          }
16008
        (void) CopyMagickString(resource_info->image_info->filename,
16009
          (char *) data,MagickPathExtent);
16010
        (void) XFree((void *) data);
16011
        nexus=ReadImage(resource_info->image_info,exception);
16012
        CatchException(exception);
16013
        if (nexus != (Image *) NULL)
16014
          *state|=NextImageState | ExitState;
16015
        break;
16016
      }
16017
      case ReparentNotify:
16018
      {
16019
        if (resource_info->debug != MagickFalse)
16020
          (void) LogMagickEvent(X11Event,GetMagickModule(),
16021
            "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16022
            event.xreparent.window);
16023
        break;
16024
      }
16025
      case UnmapNotify:
16026
      {
16027
        if (resource_info->debug != MagickFalse)
16028
          (void) LogMagickEvent(X11Event,GetMagickModule(),
16029
            "Unmap Notify: 0x%lx",event.xunmap.window);
16030
        if (event.xunmap.window == windows->backdrop.id)
16031
          {
16032
            windows->backdrop.mapped=MagickFalse;
16033
            break;
16034
          }
16035
        if (event.xunmap.window == windows->image.id)
16036
          {
16037
            windows->image.mapped=MagickFalse;
16038
            break;
16039
          }
16040
        if (event.xunmap.window == windows->magnify.id)
16041
          {
16042
            windows->magnify.mapped=MagickFalse;
16043
            break;
16044
          }
16045
        if (event.xunmap.window == windows->pan.id)
16046
          {
16047
            windows->pan.mapped=MagickFalse;
16048
            break;
16049
          }
16050
        if (event.xunmap.window == windows->info.id)
16051
          {
16052
            windows->info.mapped=MagickFalse;
16053
            break;
16054
          }
16055
        if (event.xunmap.window == windows->icon.id)
16056
          {
16057
            if (map_info->colormap == icon_map->colormap)
16058
              XConfigureImageColormap(display,resource_info,windows,
16059
                display_image,exception);
16060
            (void) XFreeStandardColormap(display,icon_visual,icon_map,
16061
              icon_pixel);
16062
            windows->icon.mapped=MagickFalse;
16063
            break;
16064
          }
16065
        if (event.xunmap.window == windows->command.id)
16066
          {
16067
            windows->command.mapped=MagickFalse;
16068
            break;
16069
          }
16070
        if (event.xunmap.window == windows->popup.id)
16071
          {
16072
            if (windows->backdrop.id != (Window) NULL)
16073
              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16074
                CurrentTime);
16075
            windows->popup.mapped=MagickFalse;
16076
            break;
16077
          }
16078
        if (event.xunmap.window == windows->widget.id)
16079
          {
16080
            if (windows->backdrop.id != (Window) NULL)
16081
              (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16082
                CurrentTime);
16083
            windows->widget.mapped=MagickFalse;
16084
            break;
16085
          }
16086
        break;
16087
      }
16088
      default:
16089
      {
16090
        if (resource_info->debug != MagickFalse)
16091
          (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16092
            event.type);
16093
        break;
16094
      }
16095
    }
16096
  } while (!(*state & ExitState));
16097
  if ((*state & ExitState) == 0)
16098
    (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16099
      &display_image,exception);
16100
  else
16101
    if (resource_info->confirm_edit != MagickFalse)
16102
      {
16103
        /*
16104
          Query user if image has changed.
16105
        */
16106
        if ((resource_info->immutable == MagickFalse) &&
16107
            display_image->taint != MagickFalse)
16108
          {
16109
            int
16110
              status;
16111
16112
            status=XConfirmWidget(display,windows,"Your image changed.",
16113
              "Do you want to save it");
16114
            if (status == 0)
16115
              *state&=(unsigned int) (~ExitState);
16116
            else
16117
              if (status > 0)
16118
                (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16119
                  &display_image,exception);
16120
          }
16121
      }
16122
  if ((windows->visual_info->klass == GrayScale) ||
16123
      (windows->visual_info->klass == PseudoColor) ||
16124
      (windows->visual_info->klass == DirectColor))
16125
    {
16126
      /*
16127
        Withdraw pan and Magnify window.
16128
      */
16129
      if (windows->info.mapped != MagickFalse)
16130
        (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16131
      if (windows->magnify.mapped != MagickFalse)
16132
        (void) XWithdrawWindow(display,windows->magnify.id,
16133
          windows->magnify.screen);
16134
      if (windows->command.mapped != MagickFalse)
16135
        (void) XWithdrawWindow(display,windows->command.id,
16136
          windows->command.screen);
16137
    }
16138
  if (windows->pan.mapped != MagickFalse)
16139
    (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16140
  if (resource_info->backdrop == MagickFalse)
16141
    if (windows->backdrop.mapped)
16142
      {
16143
        (void) XWithdrawWindow(display,windows->backdrop.id,
16144
          windows->backdrop.screen);
16145
        (void) XDestroyWindow(display,windows->backdrop.id);
16146
        windows->backdrop.id=(Window) NULL;
16147
        (void) XWithdrawWindow(display,windows->image.id,
16148
          windows->image.screen);
16149
        (void) XDestroyWindow(display,windows->image.id);
16150
        windows->image.id=(Window) NULL;
16151
      }
16152
  XSetCursorState(display,windows,MagickTrue);
16153
  XCheckRefreshWindows(display,windows);
16154
  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16155
    *state&=(unsigned int) (~ExitState);
16156
  if (*state & ExitState)
16157
    {
16158
      /*
16159
        Free Standard Colormap.
16160
      */
16161
      (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16162
      if (resource_info->map_type == (char *) NULL)
16163
        (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16164
      /*
16165
        Free X resources.
16166
      */
16167
      if (resource_info->copy_image != (Image *) NULL)
16168
        {
16169
          resource_info->copy_image=DestroyImage(resource_info->copy_image);
16170
          resource_info->copy_image=NewImageList();
16171
        }
16172
      DestroyXResources();
16173
    }
16174
  (void) XSync(display,MagickFalse);
16175
  /*
16176
    Restore our progress monitor and warning handlers.
16177
  */
16178
  (void) SetErrorHandler(warning_handler);
16179
  (void) SetWarningHandler(warning_handler);
16180
  /*
16181
    Change to home directory.
16182
  */
16183
  directory=getcwd(working_directory,MagickPathExtent);
16184
  (void) directory;
16185
  {
16186
    int
16187
      status;
16188
16189
    if (*resource_info->home_directory == '\0')
16190
      (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16191
    status=chdir(resource_info->home_directory);
16192
    if (status == -1)
16193
      (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16194
        "UnableToOpenFile","%s",resource_info->home_directory);
16195
  }
16196
  *image=display_image;
16197
  return(nexus);
16198
}
16199
#else
16200

16201
/*
16202
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16203
%                                                                             %
16204
%                                                                             %
16205
%                                                                             %
16206
+   D i s p l a y I m a g e s                                                 %
16207
%                                                                             %
16208
%                                                                             %
16209
%                                                                             %
16210
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16211
%
16212
%  DisplayImages() displays an image sequence to any X window screen.  It
16213
%  returns a value other than 0 if successful.  Check the exception member
16214
%  of image to determine the reason for any failure.
16215
%
16216
%  The format of the DisplayImages method is:
16217
%
16218
%      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16219
%        Image *images,ExceptionInfo *exception)
16220
%
16221
%  A description of each parameter follows:
16222
%
16223
%    o image_info: the image info.
16224
%
16225
%    o image: the image.
16226
%
16227
%    o exception: return any errors or warnings in this structure.
16228
%
16229
*/
16230
MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16231
  Image *image,ExceptionInfo *exception)
16232
0
{
16233
0
  assert(image_info != (const ImageInfo *) NULL);
16234
0
  assert(image_info->signature == MagickCoreSignature);
16235
0
  assert(image != (Image *) NULL);
16236
0
  assert(image->signature == MagickCoreSignature);
16237
0
  (void) image_info;
16238
0
  if (IsEventLogging() != MagickFalse)
16239
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16240
0
  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16241
0
    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16242
0
  return(MagickFalse);
16243
0
}
16244

16245
/*
16246
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16247
%                                                                             %
16248
%                                                                             %
16249
%                                                                             %
16250
+   R e m o t e D i s p l a y C o m m a n d                                   %
16251
%                                                                             %
16252
%                                                                             %
16253
%                                                                             %
16254
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16255
%
16256
%  RemoteDisplayCommand() encourages a remote display program to display the
16257
%  specified image filename.
16258
%
16259
%  The format of the RemoteDisplayCommand method is:
16260
%
16261
%      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16262
%        const char *window,const char *filename,ExceptionInfo *exception)
16263
%
16264
%  A description of each parameter follows:
16265
%
16266
%    o image_info: the image info.
16267
%
16268
%    o window: Specifies the name or id of an X window.
16269
%
16270
%    o filename: the name of the image filename to display.
16271
%
16272
%    o exception: return any errors or warnings in this structure.
16273
%
16274
*/
16275
MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16276
  const char *window,const char *filename,ExceptionInfo *exception)
16277
0
{
16278
0
  assert(image_info != (const ImageInfo *) NULL);
16279
0
  assert(image_info->signature == MagickCoreSignature);
16280
0
  assert(filename != (char *) NULL);
16281
0
  if (IsEventLogging() != MagickFalse)
16282
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16283
0
  (void) window;
16284
0
  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16285
0
    "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16286
0
  return(MagickFalse);
16287
0
}
16288
#endif