Coverage Report

Created: 2026-02-14 07:11

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