blob: 457f65586d608621224a4f3bed0646f6177aa1fc [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% AAA N N IIIII M M AAA TTTTT EEEEE %
7% A A NN N I MM MM A A T E %
8% AAAAA N N N I M M M AAAAA T EEE %
9% A A N NN I M M A A T E %
10% A A N N IIIII M M A A T EEEEE %
11% %
12% %
13% Methods to Interactively Animate an Image Sequence %
14% %
15% Software Design %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
cristyfe676ee2013-11-18 13:03:38 +000020% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/animate.h"
44#include "MagickCore/animate-private.h"
cristy228095b2012-06-21 01:42:50 +000045#include "MagickCore/attribute.h"
cristy4c08aed2011-07-01 19:47:50 +000046#include "MagickCore/client.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace.h"
cristy707f0632012-02-15 23:40:22 +000050#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000051#include "MagickCore/constitute.h"
52#include "MagickCore/delegate.h"
53#include "MagickCore/exception.h"
54#include "MagickCore/exception-private.h"
55#include "MagickCore/geometry.h"
56#include "MagickCore/image-private.h"
57#include "MagickCore/layer.h"
58#include "MagickCore/list.h"
59#include "MagickCore/log.h"
60#include "MagickCore/image.h"
61#include "MagickCore/memory_.h"
62#include "MagickCore/monitor.h"
63#include "MagickCore/monitor-private.h"
64#include "MagickCore/option.h"
65#include "MagickCore/pixel-accessor.h"
66#include "MagickCore/property.h"
67#include "MagickCore/resource_.h"
68#include "MagickCore/string_.h"
69#include "MagickCore/string-private.h"
70#include "MagickCore/transform.h"
71#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000072#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000073#include "MagickCore/version.h"
74#include "MagickCore/widget.h"
cristybcbda3f2011-09-03 13:01:22 +000075#include "MagickCore/widget-private.h"
76#include "MagickCore/xwindow.h"
cristy4c08aed2011-07-01 19:47:50 +000077#include "MagickCore/xwindow-private.h"
cristy3ed852e2009-09-05 21:47:34 +000078
79#if defined(MAGICKCORE_X11_DELEGATE)
80/*
81 Animate state declarations.
82*/
83#define AutoReverseAnimationState 0x0004
84#define ForwardAnimationState 0x0008
85#define HighlightState 0x0010
86#define PlayAnimationState 0x0020
87#define RepeatAnimationState 0x0040
88#define StepAnimationState 0x0080
89
90/*
91 Static declarations.
92*/
93static const char
94 *AnimateHelp[]=
95 {
96 "BUTTONS",
97 "",
98 " Press any button to map or unmap the Command widget.",
99 "",
100 "COMMAND WIDGET",
101 " The Command widget lists a number of sub-menus and commands.",
102 " They are",
103 "",
104 " Animate",
105 " Open...",
106 " Save...",
107 " Play",
108 " Step",
109 " Repeat",
110 " Auto Reverse",
111 " Speed",
112 " Slower",
113 " Faster",
114 " Direction",
115 " Forward",
116 " Reverse",
117 " Help",
118 " Overview",
119 " Browse Documentation",
120 " About Animate",
121 " Image Info",
122 " Quit",
123 "",
124 " Menu items with a indented triangle have a sub-menu. They",
125 " are represented above as the indented items. To access a",
126 " sub-menu item, move the pointer to the appropriate menu and",
127 " press a button and drag. When you find the desired sub-menu",
128 " item, release the button and the command is executed. Move",
129 " the pointer away from the sub-menu if you decide not to",
130 " execute a particular command.",
131 "",
132 "KEYBOARD ACCELERATORS",
133 " Accelerators are one or two key presses that effect a",
134 " particular command. The keyboard accelerators that",
135 " animate(1) understands is:",
136 "",
137 " Ctl+O Press to open an image from a file.",
138 "",
139 " space Press to display the next image in the sequence.",
140 "",
141 " < Press to speed-up the display of the images. Refer to",
142 " -delay for more information.",
143 "",
144 " > Press to slow the display of the images. Refer to",
145 " -delay for more information.",
146 "",
147 " F1 Press to display helpful information about animate(1).",
148 "",
149 " Find Press to browse documentation about ImageMagick.",
150 "",
151 " ? Press to display information about the image. Press",
152 " any key or button to erase the information.",
153 "",
154 " This information is printed: image name; image size;",
155 " and the total number of unique colors in the image.",
156 "",
157 " Ctl-q Press to discard all images and exit program.",
158 (char *) NULL
159 };
160
161/*
162 Constant declarations.
163*/
164static const char
165 *PageSizes[]=
166 {
167 "Letter",
168 "Tabloid",
169 "Ledger",
170 "Legal",
171 "Statement",
172 "Executive",
173 "A3",
174 "A4",
175 "A5",
176 "B4",
177 "B5",
178 "Folio",
179 "Quarto",
180 "10x14",
181 (char *) NULL
182 };
183
184static const unsigned char
185 HighlightBitmap[8] =
186 {
187 (unsigned char) 0xaa,
188 (unsigned char) 0x55,
189 (unsigned char) 0xaa,
190 (unsigned char) 0x55,
191 (unsigned char) 0xaa,
192 (unsigned char) 0x55,
193 (unsigned char) 0xaa,
194 (unsigned char) 0x55
195 },
196 ShadowBitmap[8] =
197 {
198 (unsigned char) 0x00,
199 (unsigned char) 0x00,
200 (unsigned char) 0x00,
201 (unsigned char) 0x00,
202 (unsigned char) 0x00,
203 (unsigned char) 0x00,
204 (unsigned char) 0x00,
205 (unsigned char) 0x00
206 };
207
208/*
209 Enumeration declarations.
210*/
211typedef enum
212{
213 OpenCommand,
214 SaveCommand,
215 PlayCommand,
216 StepCommand,
217 RepeatCommand,
218 AutoReverseCommand,
219 SlowerCommand,
220 FasterCommand,
221 ForwardCommand,
222 ReverseCommand,
223 HelpCommand,
224 BrowseDocumentationCommand,
225 VersionCommand,
226 InfoCommand,
227 QuitCommand,
228 StepBackwardCommand,
229 StepForwardCommand,
230 NullCommand
231} CommandType;
232
233/*
234 Stipples.
235*/
236#define HighlightWidth 8
237#define HighlightHeight 8
238#define ShadowWidth 8
239#define ShadowHeight 8
240
241/*
242 Forward declarations.
243*/
244static Image
245 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
cristy051718b2011-08-28 22:49:25 +0000246 Image **,MagickStatusType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000247
248static MagickBooleanType
cristy051718b2011-08-28 22:49:25 +0000249 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000250
251/*
252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253% %
254% %
255% %
256% A n i m a t e I m a g e s %
257% %
258% %
259% %
260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261%
262% AnimateImages() repeatedly displays an image sequence to any X window
263% screen. It returns a value other than 0 if successful. Check the
264% exception member of image to determine the reason for any failure.
265%
266% The format of the AnimateImages method is:
267%
268% MagickBooleanType AnimateImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +0000269% Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000270%
271% A description of each parameter follows:
272%
273% o image_info: the image info.
274%
275% o image: the image.
276%
cristy051718b2011-08-28 22:49:25 +0000277% o exception: return any errors or warnings in this structure.
278%
cristy3ed852e2009-09-05 21:47:34 +0000279*/
280MagickExport MagickBooleanType AnimateImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +0000281 Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000282{
283 char
284 *argv[1];
285
286 Display
287 *display;
288
289 MagickStatusType
290 status;
291
292 XrmDatabase
293 resource_database;
294
295 XResourceInfo
296 resource_info;
297
298 assert(image_info != (const ImageInfo *) NULL);
299 assert(image_info->signature == MagickSignature);
300 assert(images != (Image *) NULL);
301 assert(images->signature == MagickSignature);
302 if (images->debug != MagickFalse)
303 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
304 display=XOpenDisplay(image_info->server_name);
305 if (display == (Display *) NULL)
306 {
cristy051718b2011-08-28 22:49:25 +0000307 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
cristyefe601c2013-01-05 17:51:12 +0000308 "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
cristy3ed852e2009-09-05 21:47:34 +0000309 return(MagickFalse);
310 }
cristy051718b2011-08-28 22:49:25 +0000311 if (exception->severity != UndefinedException)
312 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +0000313 (void) XSetErrorHandler(XError);
314 resource_database=XGetResourceDatabase(display,GetClientName());
315 (void) ResetMagickMemory(&resource_info,0,sizeof(XResourceInfo));
316 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
317 if (image_info->page != (char *) NULL)
318 resource_info.image_geometry=AcquireString(image_info->page);
319 resource_info.immutable=MagickTrue;
320 argv[0]=AcquireString(GetClientName());
cristy051718b2011-08-28 22:49:25 +0000321 (void) XAnimateImages(display,&resource_info,argv,1,images,exception);
cristye42f6582012-02-11 17:59:50 +0000322 (void) SetErrorHandler((ErrorHandler) NULL);
323 (void) SetWarningHandler((WarningHandler) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000324 argv[0]=DestroyString(argv[0]);
325 (void) XCloseDisplay(display);
326 XDestroyResourceInfo(&resource_info);
cristy051718b2011-08-28 22:49:25 +0000327 status=exception->severity == UndefinedException ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000328 return(status != 0 ? MagickTrue : MagickFalse);
329}
330
331/*
332%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
333% %
334% %
335% %
336+ X M a g i c k C o m m a n d %
337% %
338% %
339% %
340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341%
342% XMagickCommand() makes a transform to the image or Image window as specified
343% by a user menu button or keyboard command.
344%
345% The format of the XMagickCommand method is:
346%
347% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
348% XWindows *windows,const CommandType command_type,Image **image,
cristy051718b2011-08-28 22:49:25 +0000349% MagickStatusType *state,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000350%
351% A description of each parameter follows:
352%
353% o display: Specifies a connection to an X server; returned from
354% XOpenDisplay.
355%
356% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
357%
358% o windows: Specifies a pointer to a XWindows structure.
359%
360% o image: the image; XMagickCommand
361% may transform the image and return a new image pointer.
362%
363% o state: Specifies a MagickStatusType; XMagickCommand may return a
364% modified state.
365%
cristy051718b2011-08-28 22:49:25 +0000366% o exception: return any errors or warnings in this structure.
367%
cristy3ed852e2009-09-05 21:47:34 +0000368%
369*/
370static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
371 XWindows *windows,const CommandType command_type,Image **image,
cristy051718b2011-08-28 22:49:25 +0000372 MagickStatusType *state,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000373{
374 Image
375 *nexus;
376
377 MagickBooleanType
378 proceed;
379
380 MagickStatusType
381 status;
382
383 XTextProperty
384 window_name;
385
386 /*
387 Process user command.
388 */
389 nexus=NewImageList();
390 switch (command_type)
391 {
392 case OpenCommand:
393 {
394 char
395 **filelist;
396
cristy3ed852e2009-09-05 21:47:34 +0000397 Image
398 *images,
399 *next;
400
401 ImageInfo
402 *read_info;
403
404 int
405 number_files;
406
407 register int
408 i;
409
410 static char
411 filenames[MaxTextExtent] = "*";
412
413 if (resource_info->immutable != MagickFalse)
414 break;
415 /*
416 Request file name from user.
417 */
418 XFileBrowserWidget(display,windows,"Animate",filenames);
419 if (*filenames == '\0')
420 return((Image *) NULL);
421 /*
422 Expand the filenames.
423 */
cristy73bd4a52010-10-05 11:24:23 +0000424 filelist=(char **) AcquireMagickMemory(sizeof(char *));
cristy3ed852e2009-09-05 21:47:34 +0000425 if (filelist == (char **) NULL)
426 {
427 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
428 filenames);
429 return((Image *) NULL);
430 }
431 number_files=1;
432 filelist[0]=filenames;
433 status=ExpandFilenames(&number_files,&filelist);
434 if ((status == MagickFalse) || (number_files == 0))
435 {
436 if (number_files == 0)
437 {
438 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
439 return((Image *) NULL);
440 }
441 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
442 filenames);
443 return((Image *) NULL);
444 }
445 read_info=CloneImageInfo(resource_info->image_info);
cristy3ed852e2009-09-05 21:47:34 +0000446 images=NewImageList();
447 XSetCursorState(display,windows,MagickTrue);
448 XCheckRefreshWindows(display,windows);
449 for (i=0; i < number_files; i++)
450 {
451 (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
452 filelist[i]=DestroyString(filelist[i]);
453 *read_info->magick='\0';
454 next=ReadImage(read_info,exception);
455 CatchException(exception);
456 if (next != (Image *) NULL)
457 AppendImageToList(&images,next);
458 if (number_files <= 5)
459 continue;
460 proceed=SetImageProgress(images,LoadImageTag,i,(MagickSizeType)
461 number_files);
462 if (proceed == MagickFalse)
463 break;
464 }
465 filelist=(char **) RelinquishMagickMemory(filelist);
cristy3ed852e2009-09-05 21:47:34 +0000466 read_info=DestroyImageInfo(read_info);
467 if (images == (Image *) NULL)
468 {
469 XSetCursorState(display,windows,MagickFalse);
470 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
471 return((Image *) NULL);
472 }
473 nexus=GetFirstImageInList(images);
474 *state|=ExitState;
475 break;
476 }
477 case PlayCommand:
478 {
479 char
480 basename[MaxTextExtent];
481
482 int
483 status;
484
485 /*
486 Window name is the base of the filename.
487 */
488 *state|=PlayAnimationState;
489 *state&=(~AutoReverseAnimationState);
490 GetPathComponent((*image)->magick_filename,BasePath,basename);
cristyb51dff52011-05-19 16:55:47 +0000491 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristy40a08ad2010-02-09 02:27:44 +0000492 "%s: %s",MagickPackageName,basename);
cristy3ed852e2009-09-05 21:47:34 +0000493 if (resource_info->title != (char *) NULL)
494 {
495 char
496 *title;
497
498 title=InterpretImageProperties(resource_info->image_info,*image,
cristy018f07f2011-09-04 21:15:19 +0000499 resource_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +0000500 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
501 title=DestroyString(title);
502 }
503 status=XStringListToTextProperty(&windows->image.name,1,&window_name);
504 if (status == 0)
505 break;
506 XSetWMName(display,windows->image.id,&window_name);
507 (void) XFree((void *) window_name.value);
508 break;
509 }
510 case StepCommand:
511 case StepBackwardCommand:
512 case StepForwardCommand:
513 {
514 *state|=StepAnimationState;
515 *state&=(~PlayAnimationState);
516 if (command_type == StepBackwardCommand)
517 *state&=(~ForwardAnimationState);
518 if (command_type == StepForwardCommand)
519 *state|=ForwardAnimationState;
520 if (resource_info->title != (char *) NULL)
521 break;
522 break;
523 }
524 case RepeatCommand:
525 {
526 *state|=RepeatAnimationState;
527 *state&=(~AutoReverseAnimationState);
528 *state|=PlayAnimationState;
529 break;
530 }
531 case AutoReverseCommand:
532 {
533 *state|=AutoReverseAnimationState;
534 *state&=(~RepeatAnimationState);
535 *state|=PlayAnimationState;
536 break;
537 }
538 case SaveCommand:
539 {
540 /*
541 Save image.
542 */
cristy051718b2011-08-28 22:49:25 +0000543 status=XSaveImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000544 if (status == MagickFalse)
545 {
cristyc663dbd2011-09-16 19:43:14 +0000546 char
547 message[MaxTextExtent];
548
549 (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
550 exception->reason != (char *) NULL ? exception->reason : "",
551 exception->description != (char *) NULL ? exception->description :
552 "");
553 XNoticeWidget(display,windows,"Unable to save file:",message);
cristy3ed852e2009-09-05 21:47:34 +0000554 break;
555 }
556 break;
557 }
558 case SlowerCommand:
559 {
560 resource_info->delay++;
561 break;
562 }
563 case FasterCommand:
564 {
565 if (resource_info->delay == 0)
566 break;
567 resource_info->delay--;
568 break;
569 }
570 case ForwardCommand:
571 {
572 *state=ForwardAnimationState;
573 *state&=(~AutoReverseAnimationState);
574 break;
575 }
576 case ReverseCommand:
577 {
578 *state&=(~ForwardAnimationState);
579 *state&=(~AutoReverseAnimationState);
580 break;
581 }
582 case InfoCommand:
583 {
cristy6710d842011-10-20 23:23:00 +0000584 XDisplayImageInfo(display,resource_info,windows,(Image *) NULL,*image,
585 exception);
cristy3ed852e2009-09-05 21:47:34 +0000586 break;
587 }
588 case HelpCommand:
589 {
590 /*
591 User requested help.
592 */
593 XTextViewWidget(display,resource_info,windows,MagickFalse,
594 "Help Viewer - Animate",AnimateHelp);
595 break;
596 }
597 case BrowseDocumentationCommand:
598 {
599 Atom
600 mozilla_atom;
601
602 Window
603 mozilla_window,
604 root_window;
605
606 /*
607 Browse the ImageMagick documentation.
608 */
609 root_window=XRootWindow(display,XDefaultScreen(display));
610 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
611 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
612 if (mozilla_window != (Window) NULL)
613 {
614 char
615 command[MaxTextExtent],
616 *url;
617
618 /*
619 Display documentation using Netscape remote control.
620 */
621 url=GetMagickHomeURL();
cristyb51dff52011-05-19 16:55:47 +0000622 (void) FormatLocaleString(command,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000623 "openurl(%s,new-tab)",url);
624 url=DestroyString(url);
625 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
626 (void) XChangeProperty(display,mozilla_window,mozilla_atom,
627 XA_STRING,8,PropModeReplace,(unsigned char *) command,
628 (int) strlen(command));
629 XSetCursorState(display,windows,MagickFalse);
630 break;
631 }
632 XSetCursorState(display,windows,MagickTrue);
633 XCheckRefreshWindows(display,windows);
634 status=InvokeDelegate(resource_info->image_info,*image,"browse",
cristy051718b2011-08-28 22:49:25 +0000635 (char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000636 if (status == MagickFalse)
637 XNoticeWidget(display,windows,"Unable to browse documentation",
638 (char *) NULL);
639 XDelay(display,1500);
640 XSetCursorState(display,windows,MagickFalse);
641 break;
642 }
643 case VersionCommand:
644 {
cristybb503372010-05-27 20:51:26 +0000645 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
cristy3ed852e2009-09-05 21:47:34 +0000646 GetMagickCopyright());
647 break;
648 }
649 case QuitCommand:
650 {
651 /*
652 exit program
653 */
654 if (resource_info->confirm_exit == MagickFalse)
655 XClientMessage(display,windows->image.id,windows->im_protocols,
656 windows->im_exit,CurrentTime);
657 else
658 {
659 int
660 status;
661
662 /*
663 Confirm program exit.
664 */
665 status=XConfirmWidget(display,windows,"Do you really want to exit",
666 resource_info->client_name);
667 if (status != 0)
668 XClientMessage(display,windows->image.id,windows->im_protocols,
669 windows->im_exit,CurrentTime);
670 }
671 break;
672 }
673 default:
674 break;
675 }
676 return(nexus);
677}
678
679/*
680%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
681% %
682% %
683% %
684+ X A n i m a t e B a c k g r o u n d I m a g e %
685% %
686% %
687% %
688%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
689%
690% XAnimateBackgroundImage() animates an image sequence in the background of
691% a window.
692%
693% The format of the XAnimateBackgroundImage method is:
694%
695% void XAnimateBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +0000696% XResourceInfo *resource_info,Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000697%
698% A description of each parameter follows:
699%
700% o display: Specifies a connection to an X server; returned from
701% XOpenDisplay.
702%
703% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
704%
705% o images: the image list.
706%
cristy051718b2011-08-28 22:49:25 +0000707% o exception: return any errors or warnings in this structure.
708%
cristy3ed852e2009-09-05 21:47:34 +0000709*/
710
cristybb503372010-05-27 20:51:26 +0000711static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000712{
713 if (x > y)
714 return(x);
715 return(y);
716}
717
718#if defined(__cplusplus) || defined(c_plusplus)
719extern "C" {
720#endif
721
722static int SceneCompare(const void *x,const void *y)
723{
724 const Image
725 **image_1,
726 **image_2;
727
728 image_1=(const Image **) x;
729 image_2=(const Image **) y;
730 return((int) ((*image_1)->scene-(*image_2)->scene));
731}
732
733#if defined(__cplusplus) || defined(c_plusplus)
734}
735#endif
736
737MagickExport void XAnimateBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +0000738 XResourceInfo *resource_info,Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000739{
740 char
741 geometry[MaxTextExtent],
742 visual_type[MaxTextExtent];
743
744 Image
745 *coalesce_image,
746 *display_image,
747 **image_list;
748
749 int
750 scene;
751
752 MagickStatusType
753 status;
754
755 RectangleInfo
756 geometry_info;
757
cristybb503372010-05-27 20:51:26 +0000758 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000759 i;
760
761 size_t
762 number_scenes;
763
764 static XPixelInfo
765 pixel;
766
767 static XStandardColormap
768 *map_info;
769
770 static XVisualInfo
771 *visual_info = (XVisualInfo *) NULL;
772
773 static XWindowInfo
774 window_info;
775
776 unsigned int
777 height,
778 width;
779
cristybb503372010-05-27 20:51:26 +0000780 size_t
cristy3ed852e2009-09-05 21:47:34 +0000781 delay;
782
783 Window
784 root_window;
785
786 XEvent
787 event;
788
789 XGCValues
790 context_values;
791
792 XResourceInfo
793 resources;
794
795 XWindowAttributes
796 window_attributes;
797
798 /*
799 Determine target window.
800 */
801 assert(images != (Image *) NULL);
802 assert(images->signature == MagickSignature);
803 if (images->debug != MagickFalse)
804 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
805 resources=(*resource_info);
806 window_info.id=(Window) NULL;
807 root_window=XRootWindow(display,XDefaultScreen(display));
808 if (LocaleCompare(resources.window_id,"root") == 0)
809 window_info.id=root_window;
810 else
811 {
812 if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
813 window_info.id=XWindowByID(display,root_window,
814 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
815 if (window_info.id == (Window) NULL)
816 window_info.id=
817 XWindowByName(display,root_window,resources.window_id);
818 }
819 if (window_info.id == (Window) NULL)
820 {
821 ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
822 resources.window_id);
823 return;
824 }
825 /*
826 Determine window visual id.
827 */
828 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
829 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
830 (void) CopyMagickString(visual_type,"default",MaxTextExtent);
831 status=XGetWindowAttributes(display,window_info.id,&window_attributes) != 0 ?
832 MagickTrue : MagickFalse;
833 if (status != MagickFalse)
cristyb51dff52011-05-19 16:55:47 +0000834 (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
cristy3ed852e2009-09-05 21:47:34 +0000835 XVisualIDFromVisual(window_attributes.visual));
836 if (visual_info == (XVisualInfo *) NULL)
837 {
838 /*
839 Allocate standard colormap.
840 */
841 map_info=XAllocStandardColormap();
842 if (map_info == (XStandardColormap *) NULL)
cristybeb1a6b2013-11-04 12:08:18 +0000843 ThrowXWindowFatalException(ResourceLimitFatalError,
844 "MemoryAllocationFailed",images->filename);
cristy3ed852e2009-09-05 21:47:34 +0000845 map_info->colormap=(Colormap) NULL;
cristyf2faecf2010-05-28 19:19:36 +0000846 pixel.pixels=(unsigned long *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000847 /*
848 Initialize visual info.
849 */
850 resources.map_type=(char *) NULL;
851 resources.visual_type=visual_type;
852 visual_info=XBestVisualInfo(display,map_info,&resources);
853 if (visual_info == (XVisualInfo *) NULL)
854 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
855 images->filename);
856 /*
857 Initialize window info.
858 */
859 window_info.ximage=(XImage *) NULL;
860 window_info.matte_image=(XImage *) NULL;
861 window_info.pixmap=(Pixmap) NULL;
862 window_info.matte_pixmap=(Pixmap) NULL;
863 }
864 /*
865 Free previous root colors.
866 */
867 if (window_info.id == root_window)
868 XDestroyWindowColors(display,root_window);
cristy051718b2011-08-28 22:49:25 +0000869 coalesce_image=CoalesceImages(images,exception);
cristy3ed852e2009-09-05 21:47:34 +0000870 if (coalesce_image == (Image *) NULL)
cristybeb1a6b2013-11-04 12:08:18 +0000871 ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
cristy3ed852e2009-09-05 21:47:34 +0000872 images->filename);
873 images=coalesce_image;
874 if (resources.map_type == (char *) NULL)
875 if ((visual_info->klass != TrueColor) &&
876 (visual_info->klass != DirectColor))
877 {
878 Image
879 *next;
880
881 /*
882 Determine if the sequence of images has the identical colormap.
883 */
884 for (next=images; next != (Image *) NULL; )
885 {
cristy8a46d822012-08-28 23:32:39 +0000886 next->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +0000887 if ((next->storage_class == DirectClass) ||
888 (next->colors != images->colors) ||
cristybb503372010-05-27 20:51:26 +0000889 (next->colors > (size_t) visual_info->colormap_size))
cristy3ed852e2009-09-05 21:47:34 +0000890 break;
cristybb503372010-05-27 20:51:26 +0000891 for (i=0; i < (ssize_t) images->colors; i++)
cristy101ab702011-10-13 13:06:32 +0000892 if (IsPixelInfoEquivalent(next->colormap+i,images->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000893 break;
cristybb503372010-05-27 20:51:26 +0000894 if (i < (ssize_t) images->colors)
cristy3ed852e2009-09-05 21:47:34 +0000895 break;
896 next=GetNextImageInList(next);
897 }
898 if (next != (Image *) NULL)
cristy018f07f2011-09-04 21:15:19 +0000899 (void) RemapImages(resources.quantize_info,images,(Image *) NULL,
900 exception);
cristy3ed852e2009-09-05 21:47:34 +0000901 }
902 /*
903 Sort images by increasing scene number.
904 */
905 number_scenes=GetImageListLength(images);
cristy051718b2011-08-28 22:49:25 +0000906 image_list=ImageListToArray(images,exception);
cristy3ed852e2009-09-05 21:47:34 +0000907 if (image_list == (Image **) NULL)
908 ThrowXWindowFatalException(ResourceLimitFatalError,
909 "MemoryAllocationFailed",images->filename);
cristybb503372010-05-27 20:51:26 +0000910 for (i=0; i < (ssize_t) number_scenes; i++)
cristy3ed852e2009-09-05 21:47:34 +0000911 if (image_list[i]->scene == 0)
912 break;
cristybb503372010-05-27 20:51:26 +0000913 if (i == (ssize_t) number_scenes)
cristy3ed852e2009-09-05 21:47:34 +0000914 qsort((void *) image_list,number_scenes,sizeof(Image *),SceneCompare);
915 /*
916 Initialize Standard Colormap.
917 */
918 resources.colormap=SharedColormap;
919 display_image=image_list[0];
920 for (scene=0; scene < (int) number_scenes; scene++)
921 {
922 if ((resource_info->map_type != (char *) NULL) ||
923 (visual_info->klass == TrueColor) ||
924 (visual_info->klass == DirectColor))
cristy8a46d822012-08-28 23:32:39 +0000925 (void) SetImageType(image_list[scene],image_list[scene]->alpha_trait ==
926 BlendPixelTrait ? TrueColorType : TrueColorMatteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000927 if ((display_image->columns < image_list[scene]->columns) &&
928 (display_image->rows < image_list[scene]->rows))
929 display_image=image_list[scene];
930 }
931 if ((resource_info->map_type != (char *) NULL) ||
932 (visual_info->klass == TrueColor) || (visual_info->klass == DirectColor))
cristy8a46d822012-08-28 23:32:39 +0000933 (void) SetImageType(display_image,display_image->alpha_trait !=
934 BlendPixelTrait ? TrueColorType : TrueColorMatteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000935 XMakeStandardColormap(display,visual_info,&resources,display_image,map_info,
cristy6710d842011-10-20 23:23:00 +0000936 &pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +0000937 /*
938 Graphic context superclass.
939 */
940 context_values.background=pixel.background_color.pixel;
941 context_values.foreground=pixel.foreground_color.pixel;
cristycee97112010-05-28 00:44:52 +0000942 pixel.annotate_context=XCreateGC(display,window_info.id,(unsigned long)
943 (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +0000944 if (pixel.annotate_context == (GC) NULL)
945 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
946 images->filename);
947 /*
948 Initialize Image window attributes.
949 */
950 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
951 &resources,&window_info);
952 /*
953 Create the X image.
954 */
955 window_info.width=(unsigned int) image_list[0]->columns;
956 window_info.height=(unsigned int) image_list[0]->rows;
957 if ((image_list[0]->columns != window_info.width) ||
958 (image_list[0]->rows != window_info.height))
959 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
960 image_list[0]->filename);
cristyb51dff52011-05-19 16:55:47 +0000961 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
cristy3ed852e2009-09-05 21:47:34 +0000962 window_attributes.width,window_attributes.height);
963 geometry_info.width=window_info.width;
964 geometry_info.height=window_info.height;
cristyecd0ab52010-05-30 14:59:20 +0000965 geometry_info.x=(ssize_t) window_info.x;
966 geometry_info.y=(ssize_t) window_info.y;
cristy3ed852e2009-09-05 21:47:34 +0000967 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
968 &geometry_info.width,&geometry_info.height);
969 window_info.width=(unsigned int) geometry_info.width;
970 window_info.height=(unsigned int) geometry_info.height;
971 window_info.x=(int) geometry_info.x;
972 window_info.y=(int) geometry_info.y;
973 status=XMakeImage(display,&resources,&window_info,image_list[0],
cristy051718b2011-08-28 22:49:25 +0000974 window_info.width,window_info.height,exception);
cristy3ed852e2009-09-05 21:47:34 +0000975 if (status == MagickFalse)
976 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
977 images->filename);
978 window_info.x=0;
979 window_info.y=0;
980 if (display_image->debug != MagickFalse)
981 {
982 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000983 "Image: %s[%.20g] %.20gx%.20g ",image_list[0]->filename,(double)
984 image_list[0]->scene,(double) image_list[0]->columns,(double)
985 image_list[0]->rows);
cristy3ed852e2009-09-05 21:47:34 +0000986 if (image_list[0]->colors != 0)
cristye8c25f92010-06-03 00:53:06 +0000987 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
988 image_list[0]->colors);
cristy3ed852e2009-09-05 21:47:34 +0000989 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
990 image_list[0]->magick);
991 }
992 /*
993 Adjust image dimensions as specified by backdrop or geometry options.
994 */
995 width=window_info.width;
996 height=window_info.height;
997 if (resources.backdrop != MagickFalse)
998 {
999 /*
1000 Center image on window.
1001 */
1002 window_info.x=(int) (window_attributes.width/2)-
1003 (window_info.ximage->width/2);
1004 window_info.y=(int) (window_attributes.height/2)-
1005 (window_info.ximage->height/2);
1006 width=(unsigned int) window_attributes.width;
1007 height=(unsigned int) window_attributes.height;
1008 }
1009 if (resources.image_geometry != (char *) NULL)
1010 {
1011 char
1012 default_geometry[MaxTextExtent];
1013
1014 int
1015 flags,
1016 gravity;
1017
1018 XSizeHints
1019 *size_hints;
1020
1021 /*
1022 User specified geometry.
1023 */
1024 size_hints=XAllocSizeHints();
1025 if (size_hints == (XSizeHints *) NULL)
1026 ThrowXWindowFatalException(ResourceLimitFatalError,
1027 "MemoryAllocationFailed",images->filename);
1028 size_hints->flags=0L;
cristyb51dff52011-05-19 16:55:47 +00001029 (void) FormatLocaleString(default_geometry,MaxTextExtent,"%ux%u",width,
cristy3ed852e2009-09-05 21:47:34 +00001030 height);
1031 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
1032 default_geometry,window_info.border_width,size_hints,&window_info.x,
1033 &window_info.y,(int *) &width,(int *) &height,&gravity);
1034 if (((flags & (XValue | YValue))) != 0)
1035 {
1036 width=(unsigned int) window_attributes.width;
1037 height=(unsigned int) window_attributes.height;
1038 }
1039 (void) XFree((void *) size_hints);
1040 }
1041 /*
1042 Create the X pixmap.
1043 */
1044 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
1045 (unsigned int) height,window_info.depth);
1046 if (window_info.pixmap == (Pixmap) NULL)
1047 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
1048 images->filename);
1049 /*
1050 Display pixmap on the window.
1051 */
1052 if (((unsigned int) width > window_info.width) ||
1053 ((unsigned int) height > window_info.height))
1054 (void) XFillRectangle(display,window_info.pixmap,
1055 window_info.annotate_context,0,0,(unsigned int) width,
1056 (unsigned int) height);
1057 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
1058 window_info.ximage,0,0,window_info.x,window_info.y,window_info.width,
1059 window_info.height);
1060 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
1061 (void) XClearWindow(display,window_info.id);
1062 /*
1063 Initialize image pixmaps structure.
1064 */
1065 window_info.pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1066 sizeof(*window_info.pixmaps));
1067 window_info.matte_pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1068 sizeof(*window_info.matte_pixmaps));
1069 if ((window_info.pixmaps == (Pixmap *) NULL) ||
1070 (window_info.matte_pixmaps == (Pixmap *) NULL))
1071 ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
1072 images->filename);
1073 window_info.pixmaps[0]=window_info.pixmap;
1074 window_info.matte_pixmaps[0]=window_info.pixmap;
1075 for (scene=1; scene < (int) number_scenes; scene++)
1076 {
1077 unsigned int
1078 columns,
1079 rows;
1080
1081 /*
1082 Create X image.
1083 */
1084 window_info.pixmap=(Pixmap) NULL;
1085 window_info.matte_pixmap=(Pixmap) NULL;
1086 if ((resources.map_type != (char *) NULL) ||
1087 (visual_info->klass == TrueColor) ||
1088 (visual_info->klass == DirectColor))
1089 if (image_list[scene]->storage_class == PseudoClass)
cristy4c08aed2011-07-01 19:47:50 +00001090 XGetPixelInfo(display,visual_info,map_info,&resources,
cristy3ed852e2009-09-05 21:47:34 +00001091 image_list[scene],window_info.pixel_info);
1092 columns=(unsigned int) image_list[scene]->columns;
1093 rows=(unsigned int) image_list[scene]->rows;
1094 if ((image_list[scene]->columns != columns) ||
1095 (image_list[scene]->rows != rows))
1096 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1097 image_list[scene]->filename);
1098 status=XMakeImage(display,&resources,&window_info,image_list[scene],
cristy051718b2011-08-28 22:49:25 +00001099 columns,rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00001100 if (status == MagickFalse)
1101 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1102 images->filename);
1103 if (display_image->debug != MagickFalse)
1104 {
1105 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001106 "Image: [%.20g] %s %.20gx%.20g ",(double) image_list[scene]->scene,
1107 image_list[scene]->filename,(double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001108 if (image_list[scene]->colors != 0)
cristye8c25f92010-06-03 00:53:06 +00001109 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
1110 image_list[scene]->colors);
cristy3ed852e2009-09-05 21:47:34 +00001111 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
1112 image_list[scene]->magick);
1113 }
1114 /*
1115 Create the X pixmap.
1116 */
1117 window_info.pixmap=XCreatePixmap(display,window_info.id,width,height,
1118 window_info.depth);
1119 if (window_info.pixmap == (Pixmap) NULL)
1120 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
1121 images->filename);
1122 /*
1123 Display pixmap on the window.
1124 */
1125 if ((width > window_info.width) || (height > window_info.height))
1126 (void) XFillRectangle(display,window_info.pixmap,
1127 window_info.annotate_context,0,0,width,height);
1128 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
1129 window_info.ximage,0,0,window_info.x,window_info.y,window_info.width,
1130 window_info.height);
1131 (void) XSetWindowBackgroundPixmap(display,window_info.id,
1132 window_info.pixmap);
1133 (void) XClearWindow(display,window_info.id);
1134 window_info.pixmaps[scene]=window_info.pixmap;
1135 window_info.matte_pixmaps[scene]=window_info.matte_pixmap;
cristy8a46d822012-08-28 23:32:39 +00001136 if (image_list[scene]->alpha_trait)
cristy3ed852e2009-09-05 21:47:34 +00001137 (void) XClearWindow(display,window_info.id);
1138 delay=1000*image_list[scene]->delay/MagickMax(
1139 image_list[scene]->ticks_per_second,1L);
1140 XDelay(display,resources.delay*(delay == 0 ? 10 : delay));
1141 }
1142 window_info.pixel_info=(&pixel);
1143 /*
1144 Display pixmap on the window.
1145 */
1146 (void) XSelectInput(display,window_info.id,SubstructureNotifyMask);
1147 event.type=Expose;
1148 do
1149 {
1150 for (scene=0; scene < (int) number_scenes; scene++)
1151 {
1152 if (XEventsQueued(display,QueuedAfterFlush) > 0)
1153 {
1154 (void) XNextEvent(display,&event);
1155 if (event.type == DestroyNotify)
1156 break;
1157 }
1158 window_info.pixmap=window_info.pixmaps[scene];
1159 window_info.matte_pixmap=window_info.matte_pixmaps[scene];
1160 (void) XSetWindowBackgroundPixmap(display,window_info.id,
1161 window_info.pixmap);
1162 (void) XClearWindow(display,window_info.id);
1163 (void) XSync(display,MagickFalse);
1164 delay=1000*image_list[scene]->delay/MagickMax(
1165 image_list[scene]->ticks_per_second,1L);
1166 XDelay(display,resources.delay*(delay == 0 ? 10 : delay));
1167 }
1168 } while (event.type != DestroyNotify);
1169 (void) XSync(display,MagickFalse);
1170 image_list=(Image **) RelinquishMagickMemory(image_list);
1171 images=DestroyImageList(images);
1172}
1173
1174/*
1175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176% %
1177% %
1178% %
1179+ X A n i m a t e I m a g e s %
1180% %
1181% %
1182% %
1183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184%
1185% XAnimateImages() displays an image via X11.
1186%
1187% The format of the XAnimateImages method is:
1188%
1189% Image *XAnimateImages(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00001190% char **argv,const int argc,Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001191%
1192% A description of each parameter follows:
1193%
1194% o display: Specifies a connection to an X server; returned from
1195% XOpenDisplay.
1196%
1197% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1198%
1199% o argv: Specifies the application's argument list.
1200%
1201% o argc: Specifies the number of arguments.
1202%
1203% o images: the image list.
1204%
cristy051718b2011-08-28 22:49:25 +00001205% o exception: return any errors or warnings in this structure.
1206%
cristy3ed852e2009-09-05 21:47:34 +00001207*/
1208MagickExport Image *XAnimateImages(Display *display,
cristy051718b2011-08-28 22:49:25 +00001209 XResourceInfo *resource_info,char **argv,const int argc,Image *images,
1210 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001211{
1212#define MagickMenus 4
1213#define MaXWindows 8
1214#define MagickTitle "Commands"
1215
1216 static const char
1217 *CommandMenu[]=
1218 {
1219 "Animate",
1220 "Speed",
1221 "Direction",
1222 "Help",
1223 "Image Info",
1224 "Quit",
1225 (char *) NULL
1226 },
1227 *AnimateMenu[]=
1228 {
1229 "Open...",
1230 "Play",
1231 "Step",
1232 "Repeat",
1233 "Auto Reverse",
1234 "Save...",
1235 (char *) NULL
1236 },
1237 *SpeedMenu[]=
1238 {
1239 "Faster",
1240 "Slower",
1241 (char *) NULL
1242 },
1243 *DirectionMenu[]=
1244 {
1245 "Forward",
1246 "Reverse",
1247 (char *) NULL
1248 },
1249 *HelpMenu[]=
1250 {
1251 "Overview",
1252 "Browse Documentation",
1253 "About Animate",
1254 (char *) NULL
1255 };
1256
1257 static const char
1258 **Menus[MagickMenus]=
1259 {
1260 AnimateMenu,
1261 SpeedMenu,
1262 DirectionMenu,
1263 HelpMenu
1264 };
1265
1266 static const CommandType
1267 CommandMenus[]=
1268 {
1269 NullCommand,
1270 NullCommand,
1271 NullCommand,
1272 NullCommand,
1273 InfoCommand,
1274 QuitCommand
1275 },
1276 CommandTypes[]=
1277 {
1278 OpenCommand,
1279 PlayCommand,
1280 StepCommand,
1281 RepeatCommand,
1282 AutoReverseCommand,
1283 SaveCommand
1284 },
1285 SpeedCommands[]=
1286 {
1287 FasterCommand,
1288 SlowerCommand
1289 },
1290 DirectionCommands[]=
1291 {
1292 ForwardCommand,
1293 ReverseCommand
1294 },
1295 HelpCommands[]=
1296 {
1297 HelpCommand,
1298 BrowseDocumentationCommand,
1299 VersionCommand
1300 };
1301
1302 static const CommandType
1303 *Commands[MagickMenus]=
1304 {
1305 CommandTypes,
1306 SpeedCommands,
1307 DirectionCommands,
1308 HelpCommands
1309 };
1310
1311 char
1312 command[MaxTextExtent],
cristy00976d82011-02-20 20:31:28 +00001313 *directory,
cristy3ed852e2009-09-05 21:47:34 +00001314 geometry[MaxTextExtent],
1315 resource_name[MaxTextExtent];
1316
1317 CommandType
1318 command_type;
1319
1320 Image
1321 *coalesce_image,
1322 *display_image,
1323 *image,
1324 **image_list,
1325 *nexus;
1326
1327 int
1328 status;
1329
cristy3ed852e2009-09-05 21:47:34 +00001330 KeySym
1331 key_symbol;
1332
1333 MagickStatusType
1334 context_mask,
1335 state;
1336
1337 RectangleInfo
1338 geometry_info;
1339
1340 register char
1341 *p;
1342
cristybb503372010-05-27 20:51:26 +00001343 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001344 i;
1345
cristy9d314ff2011-03-09 01:30:28 +00001346 ssize_t
1347 first_scene,
1348 iterations,
1349 scene;
1350
cristy3ed852e2009-09-05 21:47:34 +00001351 static char
1352 working_directory[MaxTextExtent];
1353
cristybb503372010-05-27 20:51:26 +00001354 static size_t
cristy3ed852e2009-09-05 21:47:34 +00001355 number_windows;
1356
1357 static XWindowInfo
1358 *magick_windows[MaXWindows];
1359
1360 time_t
1361 timestamp;
1362
cristybb503372010-05-27 20:51:26 +00001363 size_t
cristy3ed852e2009-09-05 21:47:34 +00001364 delay,
1365 number_scenes;
1366
1367 WarningHandler
1368 warning_handler;
1369
1370 Window
1371 root_window;
1372
1373 XClassHint
1374 *class_hints;
1375
1376 XEvent
1377 event;
1378
1379 XFontStruct
1380 *font_info;
1381
1382 XGCValues
1383 context_values;
1384
1385 XPixelInfo
1386 *icon_pixel,
1387 *pixel;
1388
1389 XResourceInfo
1390 *icon_resources;
1391
1392 XStandardColormap
1393 *icon_map,
1394 *map_info;
1395
1396 XTextProperty
1397 window_name;
1398
1399 XVisualInfo
1400 *icon_visual,
1401 *visual_info;
1402
1403 XWindowChanges
1404 window_changes;
1405
1406 XWindows
1407 *windows;
1408
1409 XWMHints
1410 *manager_hints;
1411
1412 assert(images != (Image *) NULL);
1413 assert(images->signature == MagickSignature);
1414 if (images->debug != MagickFalse)
1415 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1416 warning_handler=(WarningHandler) NULL;
1417 windows=XSetWindows((XWindows *) ~0);
1418 if (windows != (XWindows *) NULL)
1419 {
1420 int
1421 status;
1422
cristy8a5d7f42013-01-06 15:24:33 +00001423 if (*working_directory == '\0')
1424 (void) CopyMagickString(working_directory,".",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00001425 status=chdir(working_directory);
1426 if (status == -1)
cristy051718b2011-08-28 22:49:25 +00001427 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
1428 "UnableToOpenFile","%s",working_directory);
cristy3ed852e2009-09-05 21:47:34 +00001429 warning_handler=resource_info->display_warnings ?
1430 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
1431 warning_handler=resource_info->display_warnings ?
1432 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
1433 }
1434 else
1435 {
1436 register Image
1437 *p;
1438
1439 /*
1440 Initialize window structure.
1441 */
1442 for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1443 {
1444 if (p->storage_class == DirectClass)
1445 {
1446 resource_info->colors=0;
1447 break;
1448 }
1449 if (p->colors > resource_info->colors)
1450 resource_info->colors=p->colors;
1451 }
1452 windows=XSetWindows(XInitializeWindows(display,resource_info));
1453 if (windows == (XWindows *) NULL)
1454 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
1455 images->filename);
1456 /*
1457 Initialize window id's.
1458 */
1459 number_windows=0;
1460 magick_windows[number_windows++]=(&windows->icon);
1461 magick_windows[number_windows++]=(&windows->backdrop);
1462 magick_windows[number_windows++]=(&windows->image);
1463 magick_windows[number_windows++]=(&windows->info);
1464 magick_windows[number_windows++]=(&windows->command);
1465 magick_windows[number_windows++]=(&windows->widget);
1466 magick_windows[number_windows++]=(&windows->popup);
cristybb503372010-05-27 20:51:26 +00001467 for (i=0; i < (ssize_t) number_windows; i++)
cristy3ed852e2009-09-05 21:47:34 +00001468 magick_windows[i]->id=(Window) NULL;
1469 }
1470 /*
1471 Initialize font info.
1472 */
1473 if (windows->font_info != (XFontStruct *) NULL)
1474 (void) XFreeFont(display,windows->font_info);
1475 windows->font_info=XBestFont(display,resource_info,MagickFalse);
1476 if (windows->font_info == (XFontStruct *) NULL)
1477 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
1478 resource_info->font);
1479 /*
1480 Initialize Standard Colormap.
1481 */
1482 map_info=windows->map_info;
1483 icon_map=windows->icon_map;
1484 visual_info=windows->visual_info;
1485 icon_visual=windows->icon_visual;
1486 pixel=windows->pixel_info;
1487 icon_pixel=windows->icon_pixel;
1488 font_info=windows->font_info;
1489 icon_resources=windows->icon_resources;
1490 class_hints=windows->class_hints;
1491 manager_hints=windows->manager_hints;
1492 root_window=XRootWindow(display,visual_info->screen);
cristy051718b2011-08-28 22:49:25 +00001493 coalesce_image=CoalesceImages(images,exception);
cristy3ed852e2009-09-05 21:47:34 +00001494 if (coalesce_image == (Image *) NULL)
1495 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
1496 images->filename);
1497 images=coalesce_image;
1498 if (resource_info->map_type == (char *) NULL)
1499 if ((visual_info->klass != TrueColor) &&
1500 (visual_info->klass != DirectColor))
1501 {
1502 Image
1503 *next;
1504
1505 /*
1506 Determine if the sequence of images has the identical colormap.
1507 */
1508 for (next=images; next != (Image *) NULL; )
1509 {
cristy8a46d822012-08-28 23:32:39 +00001510 next->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001511 if ((next->storage_class == DirectClass) ||
1512 (next->colors != images->colors) ||
cristybb503372010-05-27 20:51:26 +00001513 (next->colors > (size_t) visual_info->colormap_size))
cristy3ed852e2009-09-05 21:47:34 +00001514 break;
cristybb503372010-05-27 20:51:26 +00001515 for (i=0; i < (ssize_t) images->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001516 if (IsPixelInfoEquivalent(next->colormap+i,images->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001517 break;
cristybb503372010-05-27 20:51:26 +00001518 if (i < (ssize_t) images->colors)
cristy3ed852e2009-09-05 21:47:34 +00001519 break;
1520 next=GetNextImageInList(next);
1521 }
1522 if (next != (Image *) NULL)
1523 (void) RemapImages(resource_info->quantize_info,images,
cristy018f07f2011-09-04 21:15:19 +00001524 (Image *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001525 }
1526 /*
1527 Sort images by increasing scene number.
1528 */
1529 number_scenes=GetImageListLength(images);
cristy051718b2011-08-28 22:49:25 +00001530 image_list=ImageListToArray(images,exception);
cristy3ed852e2009-09-05 21:47:34 +00001531 if (image_list == (Image **) NULL)
1532 ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
1533 images->filename);
cristybb503372010-05-27 20:51:26 +00001534 for (scene=0; scene < (ssize_t) number_scenes; scene++)
cristy3ed852e2009-09-05 21:47:34 +00001535 if (image_list[scene]->scene == 0)
1536 break;
cristybb503372010-05-27 20:51:26 +00001537 if (scene == (ssize_t) number_scenes)
cristy3ed852e2009-09-05 21:47:34 +00001538 qsort((void *) image_list,number_scenes,sizeof(Image *),SceneCompare);
1539 /*
1540 Initialize Standard Colormap.
1541 */
1542 nexus=NewImageList();
1543 display_image=image_list[0];
cristybb503372010-05-27 20:51:26 +00001544 for (scene=0; scene < (ssize_t) number_scenes; scene++)
cristy3ed852e2009-09-05 21:47:34 +00001545 {
1546 if ((resource_info->map_type != (char *) NULL) ||
1547 (visual_info->klass == TrueColor) ||
1548 (visual_info->klass == DirectColor))
cristy8a46d822012-08-28 23:32:39 +00001549 (void) SetImageType(image_list[scene],image_list[scene]->alpha_trait ==
1550 BlendPixelTrait ? TrueColorType : TrueColorMatteType,exception);
cristy3ed852e2009-09-05 21:47:34 +00001551 if ((display_image->columns < image_list[scene]->columns) &&
1552 (display_image->rows < image_list[scene]->rows))
1553 display_image=image_list[scene];
1554 }
1555 if (display_image->debug != MagickFalse)
1556 {
1557 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001558 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,(double)
1559 display_image->scene,(double) display_image->columns,(double)
1560 display_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001561 if (display_image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +00001562 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
1563 display_image->colors);
cristy3ed852e2009-09-05 21:47:34 +00001564 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
1565 display_image->magick);
1566 }
1567 XMakeStandardColormap(display,visual_info,resource_info,display_image,
cristy6710d842011-10-20 23:23:00 +00001568 map_info,pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00001569 /*
1570 Initialize graphic context.
1571 */
1572 windows->context.id=(Window) NULL;
1573 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1574 resource_info,&windows->context);
1575 (void) CloneString(&class_hints->res_name,resource_info->client_name);
1576 (void) CloneString(&class_hints->res_class,resource_info->client_name);
1577 class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
1578 manager_hints->flags=InputHint | StateHint;
1579 manager_hints->input=MagickFalse;
1580 manager_hints->initial_state=WithdrawnState;
1581 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1582 &windows->context);
1583 if (display_image->debug != MagickFalse)
1584 (void) LogMagickEvent(X11Event,GetMagickModule(),
1585 "Window id: 0x%lx (context)",windows->context.id);
1586 context_values.background=pixel->background_color.pixel;
1587 context_values.font=font_info->fid;
1588 context_values.foreground=pixel->foreground_color.pixel;
1589 context_values.graphics_exposures=MagickFalse;
1590 context_mask=(MagickStatusType)
1591 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
1592 if (pixel->annotate_context != (GC) NULL)
1593 (void) XFreeGC(display,pixel->annotate_context);
1594 pixel->annotate_context=
1595 XCreateGC(display,windows->context.id,context_mask,&context_values);
1596 if (pixel->annotate_context == (GC) NULL)
1597 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1598 images->filename);
1599 context_values.background=pixel->depth_color.pixel;
1600 if (pixel->widget_context != (GC) NULL)
1601 (void) XFreeGC(display,pixel->widget_context);
1602 pixel->widget_context=
1603 XCreateGC(display,windows->context.id,context_mask,&context_values);
1604 if (pixel->widget_context == (GC) NULL)
1605 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1606 images->filename);
1607 context_values.background=pixel->foreground_color.pixel;
1608 context_values.foreground=pixel->background_color.pixel;
1609 context_values.plane_mask=
1610 context_values.background ^ context_values.foreground;
1611 if (pixel->highlight_context != (GC) NULL)
1612 (void) XFreeGC(display,pixel->highlight_context);
1613 pixel->highlight_context=XCreateGC(display,windows->context.id,
cristybb503372010-05-27 20:51:26 +00001614 (size_t) (context_mask | GCPlaneMask),&context_values);
cristy3ed852e2009-09-05 21:47:34 +00001615 if (pixel->highlight_context == (GC) NULL)
1616 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1617 images->filename);
1618 (void) XDestroyWindow(display,windows->context.id);
1619 /*
1620 Initialize icon window.
1621 */
1622 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
1623 icon_resources,&windows->icon);
1624 windows->icon.geometry=resource_info->icon_geometry;
1625 XBestIconSize(display,&windows->icon,display_image);
1626 windows->icon.attributes.colormap=
1627 XDefaultColormap(display,icon_visual->screen);
1628 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
1629 manager_hints->flags=InputHint | StateHint;
1630 manager_hints->input=MagickFalse;
1631 manager_hints->initial_state=IconicState;
1632 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1633 &windows->icon);
1634 if (display_image->debug != MagickFalse)
1635 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
1636 windows->icon.id);
1637 /*
1638 Initialize graphic context for icon window.
1639 */
1640 if (icon_pixel->annotate_context != (GC) NULL)
1641 (void) XFreeGC(display,icon_pixel->annotate_context);
1642 context_values.background=icon_pixel->background_color.pixel;
1643 context_values.foreground=icon_pixel->foreground_color.pixel;
1644 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
cristybb503372010-05-27 20:51:26 +00001645 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +00001646 if (icon_pixel->annotate_context == (GC) NULL)
1647 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1648 images->filename);
1649 windows->icon.annotate_context=icon_pixel->annotate_context;
1650 /*
1651 Initialize Image window.
1652 */
1653 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1654 resource_info,&windows->image);
1655 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
1656 if (resource_info->use_shared_memory == MagickFalse)
1657 windows->image.shared_memory=MagickFalse;
1658 if (resource_info->title != (char *) NULL)
1659 {
1660 char
1661 *title;
1662
1663 title=InterpretImageProperties(resource_info->image_info,display_image,
cristy018f07f2011-09-04 21:15:19 +00001664 resource_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +00001665 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
1666 (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
1667 title=DestroyString(title);
1668 }
1669 else
1670 {
1671 char
1672 filename[MaxTextExtent];
1673
1674 /*
1675 Window name is the base of the filename.
1676 */
1677 GetPathComponent(display_image->magick_filename,TailPath,filename);
cristyb51dff52011-05-19 16:55:47 +00001678 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristy04d55062011-03-15 18:58:50 +00001679 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,(double)
cristye8c25f92010-06-03 00:53:06 +00001680 display_image->scene,(double) number_scenes);
cristy3ed852e2009-09-05 21:47:34 +00001681 (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
1682 }
1683 if (resource_info->immutable != MagickFalse)
1684 windows->image.immutable=MagickTrue;
1685 windows->image.shape=MagickTrue;
1686 windows->image.geometry=resource_info->image_geometry;
cristyb51dff52011-05-19 16:55:47 +00001687 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
cristy3ed852e2009-09-05 21:47:34 +00001688 XDisplayWidth(display,visual_info->screen),
1689 XDisplayHeight(display,visual_info->screen));
1690 geometry_info.width=display_image->columns;
1691 geometry_info.height=display_image->rows;
1692 geometry_info.x=0;
1693 geometry_info.y=0;
1694 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
1695 &geometry_info.width,&geometry_info.height);
1696 windows->image.width=(unsigned int) geometry_info.width;
1697 windows->image.height=(unsigned int) geometry_info.height;
1698 windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1699 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
1700 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
1701 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
1702 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1703 resource_info,&windows->backdrop);
1704 if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
1705 {
1706 /*
1707 Initialize backdrop window.
1708 */
1709 windows->backdrop.x=0;
1710 windows->backdrop.y=0;
1711 (void) CloneString(&windows->backdrop.name,"ImageMagick Backdrop");
cristybb503372010-05-27 20:51:26 +00001712 windows->backdrop.flags=(size_t) (USSize | USPosition);
cristy3ed852e2009-09-05 21:47:34 +00001713 windows->backdrop.width=(unsigned int)
1714 XDisplayWidth(display,visual_info->screen);
1715 windows->backdrop.height=(unsigned int)
1716 XDisplayHeight(display,visual_info->screen);
1717 windows->backdrop.border_width=0;
1718 windows->backdrop.immutable=MagickTrue;
1719 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
1720 ButtonReleaseMask;
1721 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
1722 StructureNotifyMask;
1723 manager_hints->flags=IconWindowHint | InputHint | StateHint;
1724 manager_hints->icon_window=windows->icon.id;
1725 manager_hints->input=MagickTrue;
1726 manager_hints->initial_state=
1727 resource_info->iconic ? IconicState : NormalState;
1728 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1729 &windows->backdrop);
1730 if (display_image->debug != MagickFalse)
1731 (void) LogMagickEvent(X11Event,GetMagickModule(),
1732 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
1733 (void) XMapWindow(display,windows->backdrop.id);
1734 (void) XClearWindow(display,windows->backdrop.id);
1735 if (windows->image.id != (Window) NULL)
1736 {
1737 (void) XDestroyWindow(display,windows->image.id);
1738 windows->image.id=(Window) NULL;
1739 }
1740 /*
1741 Position image in the center the backdrop.
1742 */
1743 windows->image.flags|=USPosition;
1744 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
1745 (windows->image.width/2);
1746 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
1747 (windows->image.height/2);
1748 }
1749 manager_hints->flags=IconWindowHint | InputHint | StateHint;
1750 manager_hints->icon_window=windows->icon.id;
1751 manager_hints->input=MagickTrue;
1752 manager_hints->initial_state=
1753 resource_info->iconic ? IconicState : NormalState;
1754 if (windows->group_leader.id != (Window) NULL)
1755 {
1756 /*
1757 Follow the leader.
1758 */
1759 manager_hints->flags|=(MagickStatusType) WindowGroupHint;
1760 manager_hints->window_group=windows->group_leader.id;
1761 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
1762 if (display_image->debug != MagickFalse)
1763 (void) LogMagickEvent(X11Event,GetMagickModule(),
1764 "Window id: 0x%lx (group leader)",windows->group_leader.id);
1765 }
1766 XMakeWindow(display,
1767 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
1768 argv,argc,class_hints,manager_hints,&windows->image);
1769 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
1770 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
1771 if (windows->group_leader.id != (Window) NULL)
1772 (void) XSetTransientForHint(display,windows->image.id,
1773 windows->group_leader.id);
1774 if (display_image->debug != MagickFalse)
1775 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
1776 windows->image.id);
1777 /*
1778 Initialize Info widget.
1779 */
1780 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1781 resource_info,&windows->info);
1782 (void) CloneString(&windows->info.name,"Info");
1783 (void) CloneString(&windows->info.icon_name,"Info");
1784 windows->info.border_width=1;
1785 windows->info.x=2;
1786 windows->info.y=2;
1787 windows->info.flags|=PPosition;
1788 windows->info.attributes.win_gravity=UnmapGravity;
1789 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
1790 StructureNotifyMask;
1791 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1792 manager_hints->input=MagickFalse;
1793 manager_hints->initial_state=NormalState;
1794 manager_hints->window_group=windows->image.id;
1795 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
1796 &windows->info);
1797 windows->info.highlight_stipple=XCreateBitmapFromData(display,
1798 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
1799 windows->info.shadow_stipple=XCreateBitmapFromData(display,
1800 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1801 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
1802 if (windows->image.mapped)
1803 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
1804 if (display_image->debug != MagickFalse)
1805 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
1806 windows->info.id);
1807 /*
1808 Initialize Command widget.
1809 */
1810 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1811 resource_info,&windows->command);
1812 windows->command.data=MagickMenus;
1813 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001814 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
cristy3ed852e2009-09-05 21:47:34 +00001815 resource_info->client_name);
1816 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
1817 resource_name,"geometry",(char *) NULL);
1818 (void) CloneString(&windows->command.name,MagickTitle);
1819 windows->command.border_width=0;
1820 windows->command.flags|=PPosition;
1821 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1822 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
1823 OwnerGrabButtonMask | StructureNotifyMask;
1824 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1825 manager_hints->input=MagickTrue;
1826 manager_hints->initial_state=NormalState;
1827 manager_hints->window_group=windows->image.id;
1828 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1829 &windows->command);
1830 windows->command.highlight_stipple=XCreateBitmapFromData(display,
1831 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
1832 HighlightHeight);
1833 windows->command.shadow_stipple=XCreateBitmapFromData(display,
1834 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1835 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
1836 if (display_image->debug != MagickFalse)
1837 (void) LogMagickEvent(X11Event,GetMagickModule(),
1838 "Window id: 0x%lx (command)",windows->command.id);
1839 /*
1840 Initialize Widget window.
1841 */
1842 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1843 resource_info,&windows->widget);
cristyb51dff52011-05-19 16:55:47 +00001844 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
cristy3ed852e2009-09-05 21:47:34 +00001845 resource_info->client_name);
1846 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
1847 resource_name,"geometry",(char *) NULL);
1848 windows->widget.border_width=0;
1849 windows->widget.flags|=PPosition;
1850 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1851 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
1852 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
1853 StructureNotifyMask;
1854 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1855 manager_hints->input=MagickTrue;
1856 manager_hints->initial_state=NormalState;
1857 manager_hints->window_group=windows->image.id;
1858 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1859 &windows->widget);
1860 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
1861 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
1862 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
1863 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1864 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
1865 if (display_image->debug != MagickFalse)
1866 (void) LogMagickEvent(X11Event,GetMagickModule(),
1867 "Window id: 0x%lx (widget)",windows->widget.id);
1868 /*
1869 Initialize popup window.
1870 */
1871 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1872 resource_info,&windows->popup);
1873 windows->popup.border_width=0;
1874 windows->popup.flags|=PPosition;
1875 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1876 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
1877 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
1878 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1879 manager_hints->input=MagickTrue;
1880 manager_hints->initial_state=NormalState;
1881 manager_hints->window_group=windows->image.id;
1882 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1883 &windows->popup);
1884 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
1885 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
1886 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
1887 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1888 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
1889 if (display_image->debug != MagickFalse)
1890 (void) LogMagickEvent(X11Event,GetMagickModule(),
1891 "Window id: 0x%lx (pop up)",windows->popup.id);
cristy3ed852e2009-09-05 21:47:34 +00001892 /*
1893 Set out progress and warning handlers.
1894 */
1895 if (warning_handler == (WarningHandler) NULL)
1896 {
1897 warning_handler=resource_info->display_warnings ?
1898 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
1899 warning_handler=resource_info->display_warnings ?
1900 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
1901 }
1902 /*
1903 Initialize X image structure.
1904 */
1905 windows->image.x=0;
1906 windows->image.y=0;
cristy3ed852e2009-09-05 21:47:34 +00001907 /*
1908 Initialize image pixmaps structure.
1909 */
1910 window_changes.width=(int) windows->image.width;
1911 window_changes.height=(int) windows->image.height;
1912 (void) XReconfigureWMWindow(display,windows->image.id,windows->command.screen,
1913 (unsigned int) (CWWidth | CWHeight),&window_changes);
cristy3ed852e2009-09-05 21:47:34 +00001914 windows->image.pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1915 sizeof(*windows->image.pixmaps));
1916 windows->image.matte_pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1917 sizeof(*windows->image.pixmaps));
1918 if ((windows->image.pixmaps == (Pixmap *) NULL) ||
1919 (windows->image.matte_pixmaps == (Pixmap *) NULL))
1920 ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
1921 images->filename);
cristy04e11fd2010-03-19 16:01:44 +00001922 if ((windows->image.mapped == MagickFalse) ||
1923 (windows->backdrop.id != (Window) NULL))
1924 (void) XMapWindow(display,windows->image.id);
1925 XSetCursorState(display,windows,MagickTrue);
cristybb503372010-05-27 20:51:26 +00001926 for (scene=0; scene < (ssize_t) number_scenes; scene++)
cristy3ed852e2009-09-05 21:47:34 +00001927 {
1928 unsigned int
1929 columns,
1930 rows;
1931
1932 /*
1933 Create X image.
1934 */
cristy3ed852e2009-09-05 21:47:34 +00001935 windows->image.pixmap=(Pixmap) NULL;
1936 windows->image.matte_pixmap=(Pixmap) NULL;
1937 if ((resource_info->map_type != (char *) NULL) ||
1938 (visual_info->klass == TrueColor) ||
1939 (visual_info->klass == DirectColor))
1940 if (image_list[scene]->storage_class == PseudoClass)
cristy4c08aed2011-07-01 19:47:50 +00001941 XGetPixelInfo(display,visual_info,map_info,resource_info,
cristy3ed852e2009-09-05 21:47:34 +00001942 image_list[scene],windows->image.pixel_info);
1943 columns=(unsigned int) image_list[scene]->columns;
1944 rows=(unsigned int) image_list[scene]->rows;
1945 if ((image_list[scene]->columns != columns) ||
1946 (image_list[scene]->rows != rows))
1947 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1948 image_list[scene]->filename);
1949 status=XMakeImage(display,resource_info,&windows->image,image_list[scene],
cristy051718b2011-08-28 22:49:25 +00001950 columns,rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00001951 if (status == MagickFalse)
1952 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1953 images->filename);
1954 if (image_list[scene]->debug != MagickFalse)
1955 {
1956 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001957 "Image: [%.20g] %s %.20gx%.20g ",(double) image_list[scene]->scene,
1958 image_list[scene]->filename,(double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001959 if (image_list[scene]->colors != 0)
cristye8c25f92010-06-03 00:53:06 +00001960 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
1961 image_list[scene]->colors);
cristy3ed852e2009-09-05 21:47:34 +00001962 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
1963 image_list[scene]->magick);
1964 }
1965 /*
1966 Window name is the base of the filename.
1967 */
1968 if (resource_info->title != (char *) NULL)
1969 {
1970 char
1971 *title;
1972
1973 title=InterpretImageProperties(resource_info->image_info,
cristy018f07f2011-09-04 21:15:19 +00001974 image_list[scene],resource_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +00001975 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
1976 title=DestroyString(title);
1977 }
1978 else
1979 {
1980 p=image_list[scene]->magick_filename+
1981 strlen(image_list[scene]->magick_filename)-1;
1982 while ((p > image_list[scene]->magick_filename) && (*(p-1) != '/'))
1983 p--;
cristyb51dff52011-05-19 16:55:47 +00001984 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001985 "%s: %s[%.20g of %.20g]",MagickPackageName,p,(double) scene+1,
1986 (double) number_scenes);
cristy3ed852e2009-09-05 21:47:34 +00001987 }
1988 status=XStringListToTextProperty(&windows->image.name,1,&window_name);
1989 if (status != Success)
1990 {
1991 XSetWMName(display,windows->image.id,&window_name);
1992 (void) XFree((void *) window_name.value);
1993 }
1994 windows->image.pixmaps[scene]=windows->image.pixmap;
1995 windows->image.matte_pixmaps[scene]=windows->image.matte_pixmap;
cristy04e11fd2010-03-19 16:01:44 +00001996 if (scene == 0)
cristy3ed852e2009-09-05 21:47:34 +00001997 {
cristy04e11fd2010-03-19 16:01:44 +00001998 event.xexpose.x=0;
1999 event.xexpose.y=0;
2000 event.xexpose.width=(int) image_list[scene]->columns;
2001 event.xexpose.height=(int) image_list[scene]->rows;
2002 XRefreshWindow(display,&windows->image,&event);
2003 (void) XSync(display,MagickFalse);
2004 }
cristy3ed852e2009-09-05 21:47:34 +00002005 }
cristy04e11fd2010-03-19 16:01:44 +00002006 XSetCursorState(display,windows,MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00002007 if (windows->command.mapped)
2008 (void) XMapRaised(display,windows->command.id);
2009 /*
2010 Respond to events.
2011 */
2012 nexus=NewImageList();
2013 scene=0;
2014 first_scene=0;
2015 iterations=0;
2016 image=image_list[0];
2017 state=(MagickStatusType) (ForwardAnimationState | RepeatAnimationState);
2018 (void) XMagickCommand(display,resource_info,windows,PlayCommand,&images,
cristy051718b2011-08-28 22:49:25 +00002019 &state,exception);
cristy3ed852e2009-09-05 21:47:34 +00002020 do
2021 {
2022 if (XEventsQueued(display,QueuedAfterFlush) == 0)
2023 if ((state & PlayAnimationState) || (state & StepAnimationState))
2024 {
2025 MagickBooleanType
2026 pause;
2027
2028 pause=MagickFalse;
2029 delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
2030 XDelay(display,resource_info->delay*(delay == 0 ? 10 : delay));
2031 if (state & ForwardAnimationState)
2032 {
2033 /*
2034 Forward animation: increment scene number.
2035 */
cristybb503372010-05-27 20:51:26 +00002036 if (scene < ((ssize_t) number_scenes-1))
cristy3ed852e2009-09-05 21:47:34 +00002037 scene++;
2038 else
2039 {
2040 iterations++;
cristybb503372010-05-27 20:51:26 +00002041 if (iterations == (ssize_t) image_list[0]->iterations)
cristy3ed852e2009-09-05 21:47:34 +00002042 {
2043 iterations=0;
2044 state|=ExitState;
2045 }
cristy04e11fd2010-03-19 16:01:44 +00002046 if ((state & AutoReverseAnimationState) != 0)
cristy3ed852e2009-09-05 21:47:34 +00002047 {
2048 state&=(~ForwardAnimationState);
2049 scene--;
2050 }
2051 else
2052 {
cristy04e11fd2010-03-19 16:01:44 +00002053 if ((state & RepeatAnimationState) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002054 state&=(~PlayAnimationState);
2055 scene=first_scene;
2056 pause=MagickTrue;
2057 }
2058 }
2059 }
2060 else
2061 {
2062 /*
2063 Reverse animation: decrement scene number.
2064 */
2065 if (scene > first_scene)
2066 scene--;
2067 else
2068 {
2069 iterations++;
cristybb503372010-05-27 20:51:26 +00002070 if (iterations == (ssize_t) image_list[0]->iterations)
cristy3ed852e2009-09-05 21:47:34 +00002071 {
2072 iterations=0;
2073 state&=(~RepeatAnimationState);
2074 }
2075 if (state & AutoReverseAnimationState)
2076 {
2077 state|=ForwardAnimationState;
2078 scene=first_scene;
2079 pause=MagickTrue;
2080 }
2081 else
2082 {
2083 if ((state & RepeatAnimationState) == MagickFalse)
2084 state&=(~PlayAnimationState);
cristybb503372010-05-27 20:51:26 +00002085 scene=(ssize_t) number_scenes-1;
cristy3ed852e2009-09-05 21:47:34 +00002086 }
2087 }
2088 }
2089 scene=MagickMax(scene,0);
2090 image=image_list[scene];
2091 if ((image != (Image *) NULL) && (image->start_loop != 0))
2092 first_scene=scene;
2093 if ((state & StepAnimationState) ||
2094 (resource_info->title != (char *) NULL))
2095 {
2096 /*
2097 Update window title.
2098 */
2099 p=image_list[scene]->filename+
2100 strlen(image_list[scene]->filename)-1;
2101 while ((p > image_list[scene]->filename) && (*(p-1) != '/'))
2102 p--;
cristyb51dff52011-05-19 16:55:47 +00002103 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002104 "%s: %s[%.20g of %.20g]",MagickPackageName,p,(double)
2105 scene+1,(double) number_scenes);
cristy3ed852e2009-09-05 21:47:34 +00002106 if (resource_info->title != (char *) NULL)
2107 {
2108 char
2109 *title;
2110
2111 title=InterpretImageProperties(resource_info->image_info,
cristy018f07f2011-09-04 21:15:19 +00002112 image,resource_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +00002113 (void) CopyMagickString(windows->image.name,title,
2114 MaxTextExtent);
2115 title=DestroyString(title);
2116 }
2117 status=XStringListToTextProperty(&windows->image.name,1,
2118 &window_name);
2119 if (status != Success)
2120 {
2121 XSetWMName(display,windows->image.id,&window_name);
2122 (void) XFree((void *) window_name.value);
2123 }
2124 }
2125 /*
2126 Copy X pixmap to Image window.
2127 */
cristy4c08aed2011-07-01 19:47:50 +00002128 XGetPixelInfo(display,visual_info,map_info,resource_info,
cristy3ed852e2009-09-05 21:47:34 +00002129 image_list[scene],windows->image.pixel_info);
2130 windows->image.ximage->width=(int) image->columns;
2131 windows->image.ximage->height=(int) image->rows;
2132 windows->image.pixmap=windows->image.pixmaps[scene];
2133 windows->image.matte_pixmap=windows->image.matte_pixmaps[scene];
2134 event.xexpose.x=0;
2135 event.xexpose.y=0;
2136 event.xexpose.width=(int) image->columns;
2137 event.xexpose.height=(int) image->rows;
cristy04e11fd2010-03-19 16:01:44 +00002138 if ((state & ExitState) == 0)
2139 {
2140 XRefreshWindow(display,&windows->image,&event);
2141 (void) XSync(display,MagickFalse);
2142 }
cristy3ed852e2009-09-05 21:47:34 +00002143 state&=(~StepAnimationState);
2144 if (pause != MagickFalse)
cristybb503372010-05-27 20:51:26 +00002145 for (i=0; i < (ssize_t) resource_info->pause; i++)
cristy3ed852e2009-09-05 21:47:34 +00002146 {
2147 int
2148 status;
2149
2150 status=XCheckTypedWindowEvent(display,windows->image.id,KeyPress,
2151 &event);
2152 if (status != 0)
2153 {
2154 int
2155 length;
2156
2157 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2158 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2159 *(command+length)='\0';
2160 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
2161 {
2162 XClientMessage(display,windows->image.id,
2163 windows->im_protocols,windows->im_exit,CurrentTime);
2164 break;
2165 }
2166 }
2167 (void) sleep(1);
2168 }
2169 continue;
2170 }
2171 /*
2172 Handle a window event.
2173 */
2174 timestamp=time((time_t *) NULL);
2175 (void) XNextEvent(display,&event);
2176 if (windows->image.stasis == MagickFalse)
2177 windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
2178 MagickTrue : MagickFalse;
2179 if (event.xany.window == windows->command.id)
2180 {
2181 int
2182 id;
2183
2184 /*
2185 Select a command from the Command widget.
2186 */
2187 id=XCommandWidget(display,windows,CommandMenu,&event);
2188 if (id < 0)
2189 continue;
2190 (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
2191 command_type=CommandMenus[id];
2192 if (id < MagickMenus)
2193 {
2194 int
2195 entry;
2196
2197 /*
2198 Select a command from a pop-up menu.
2199 */
2200 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
2201 command);
2202 if (entry < 0)
2203 continue;
2204 (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
2205 command_type=Commands[id][entry];
2206 }
2207 if (command_type != NullCommand)
2208 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00002209 command_type,&image,&state,exception);
cristy3ed852e2009-09-05 21:47:34 +00002210 continue;
2211 }
2212 switch (event.type)
2213 {
2214 case ButtonPress:
2215 {
2216 if (display_image->debug != MagickFalse)
2217 (void) LogMagickEvent(X11Event,GetMagickModule(),
2218 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
2219 event.xbutton.button,event.xbutton.x,event.xbutton.y);
2220 if ((event.xbutton.button == Button3) &&
2221 (event.xbutton.state & Mod1Mask))
2222 {
2223 /*
2224 Convert Alt-Button3 to Button2.
2225 */
2226 event.xbutton.button=Button2;
2227 event.xbutton.state&=(~Mod1Mask);
2228 }
2229 if (event.xbutton.window == windows->backdrop.id)
2230 {
2231 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
2232 event.xbutton.time);
2233 break;
2234 }
2235 if (event.xbutton.window == windows->image.id)
2236 {
2237 if (resource_info->immutable != MagickFalse)
2238 {
2239 state|=ExitState;
2240 break;
2241 }
2242 /*
2243 Map/unmap Command widget.
2244 */
2245 if (windows->command.mapped)
2246 (void) XWithdrawWindow(display,windows->command.id,
2247 windows->command.screen);
2248 else
2249 {
2250 (void) XCommandWidget(display,windows,CommandMenu,
2251 (XEvent *) NULL);
2252 (void) XMapRaised(display,windows->command.id);
2253 }
2254 }
2255 break;
2256 }
2257 case ButtonRelease:
2258 {
2259 if (display_image->debug != MagickFalse)
2260 (void) LogMagickEvent(X11Event,GetMagickModule(),
2261 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
2262 event.xbutton.button,event.xbutton.x,event.xbutton.y);
2263 break;
2264 }
2265 case ClientMessage:
2266 {
2267 if (display_image->debug != MagickFalse)
2268 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002269 "Client Message: 0x%lx 0x%lx %d 0x%lx",(unsigned long)
2270 event.xclient.window,(unsigned long) event.xclient.message_type,
2271 event.xclient.format,(unsigned long) event.xclient.data.l[0]);
cristy3ed852e2009-09-05 21:47:34 +00002272 if (event.xclient.message_type == windows->im_protocols)
2273 {
cristyecd0ab52010-05-30 14:59:20 +00002274 if (*event.xclient.data.l == (long) windows->im_update_colormap)
cristy3ed852e2009-09-05 21:47:34 +00002275 {
2276 /*
2277 Update graphic context and window colormap.
2278 */
cristybb503372010-05-27 20:51:26 +00002279 for (i=0; i < (ssize_t) number_windows; i++)
cristy3ed852e2009-09-05 21:47:34 +00002280 {
2281 if (magick_windows[i]->id == windows->icon.id)
2282 continue;
2283 context_values.background=pixel->background_color.pixel;
2284 context_values.foreground=pixel->foreground_color.pixel;
2285 (void) XChangeGC(display,magick_windows[i]->annotate_context,
2286 context_mask,&context_values);
2287 (void) XChangeGC(display,magick_windows[i]->widget_context,
2288 context_mask,&context_values);
2289 context_values.background=pixel->foreground_color.pixel;
2290 context_values.foreground=pixel->background_color.pixel;
2291 context_values.plane_mask=
2292 context_values.background ^ context_values.foreground;
2293 (void) XChangeGC(display,magick_windows[i]->highlight_context,
cristybb503372010-05-27 20:51:26 +00002294 (size_t) (context_mask | GCPlaneMask),
cristy3ed852e2009-09-05 21:47:34 +00002295 &context_values);
2296 magick_windows[i]->attributes.background_pixel=
2297 pixel->background_color.pixel;
2298 magick_windows[i]->attributes.border_pixel=
2299 pixel->border_color.pixel;
2300 magick_windows[i]->attributes.colormap=map_info->colormap;
2301 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
cristy49e2d862010-11-12 02:50:30 +00002302 (unsigned long) magick_windows[i]->mask,
2303 &magick_windows[i]->attributes);
cristy3ed852e2009-09-05 21:47:34 +00002304 }
2305 if (windows->backdrop.id != (Window) NULL)
2306 (void) XInstallColormap(display,map_info->colormap);
2307 break;
2308 }
cristyecd0ab52010-05-30 14:59:20 +00002309 if (*event.xclient.data.l == (long) windows->im_exit)
cristy3ed852e2009-09-05 21:47:34 +00002310 {
2311 state|=ExitState;
2312 break;
2313 }
2314 break;
2315 }
2316 if (event.xclient.message_type == windows->dnd_protocols)
2317 {
2318 Atom
2319 selection,
2320 type;
2321
2322 int
2323 format,
2324 status;
2325
2326 unsigned char
2327 *data;
2328
cristyf2faecf2010-05-28 19:19:36 +00002329 unsigned long
cristy3ed852e2009-09-05 21:47:34 +00002330 after,
2331 length;
2332
2333 /*
2334 Display image named by the Drag-and-Drop selection.
2335 */
2336 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
2337 break;
2338 selection=XInternAtom(display,"DndSelection",MagickFalse);
2339 status=XGetWindowProperty(display,root_window,selection,0L,2047L,
2340 MagickFalse,(Atom) AnyPropertyType,&type,&format,&length,&after,
2341 &data);
2342 if ((status != Success) || (length == 0))
2343 break;
2344 if (*event.xclient.data.l == 2)
2345 {
2346 /*
2347 Offix DND.
2348 */
2349 (void) CopyMagickString(resource_info->image_info->filename,
2350 (char *) data,MaxTextExtent);
2351 }
2352 else
2353 {
2354 /*
2355 XDND.
2356 */
2357 if (LocaleNCompare((char *) data,"file:",5) != 0)
2358 {
2359 (void) XFree((void *) data);
2360 break;
2361 }
2362 (void) CopyMagickString(resource_info->image_info->filename,
2363 ((char *) data)+5,MaxTextExtent);
2364 }
cristy051718b2011-08-28 22:49:25 +00002365 nexus=ReadImage(resource_info->image_info,exception);
2366 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002367 if (nexus != (Image *) NULL)
2368 state|=ExitState;
2369 (void) XFree((void *) data);
2370 break;
2371 }
2372 /*
2373 If client window delete message, exit.
2374 */
2375 if (event.xclient.message_type != windows->wm_protocols)
2376 break;
cristycee97112010-05-28 00:44:52 +00002377 if (*event.xclient.data.l == (long) windows->wm_take_focus)
cristy3ed852e2009-09-05 21:47:34 +00002378 {
2379 (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
2380 (Time) event.xclient.data.l[1]);
2381 break;
2382 }
cristycee97112010-05-28 00:44:52 +00002383 if (*event.xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +00002384 break;
2385 (void) XWithdrawWindow(display,event.xclient.window,
2386 visual_info->screen);
2387 if (event.xclient.window == windows->image.id)
2388 {
2389 state|=ExitState;
2390 break;
2391 }
2392 break;
2393 }
2394 case ConfigureNotify:
2395 {
2396 if (display_image->debug != MagickFalse)
2397 (void) LogMagickEvent(X11Event,GetMagickModule(),
2398 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
2399 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
2400 event.xconfigure.y,event.xconfigure.send_event);
2401 if (event.xconfigure.window == windows->image.id)
2402 {
2403 if (event.xconfigure.send_event != 0)
2404 {
2405 XWindowChanges
2406 window_changes;
2407
2408 /*
2409 Position the transient windows relative of the Image window.
2410 */
2411 if (windows->command.geometry == (char *) NULL)
2412 if (windows->command.mapped == MagickFalse)
2413 {
2414 windows->command.x=
2415 event.xconfigure.x-windows->command.width-25;
2416 windows->command.y=event.xconfigure.y;
2417 XConstrainWindowPosition(display,&windows->command);
2418 window_changes.x=windows->command.x;
2419 window_changes.y=windows->command.y;
2420 (void) XReconfigureWMWindow(display,windows->command.id,
2421 windows->command.screen,(unsigned int) (CWX | CWY),
2422 &window_changes);
2423 }
2424 if (windows->widget.geometry == (char *) NULL)
2425 if (windows->widget.mapped == MagickFalse)
2426 {
2427 windows->widget.x=
2428 event.xconfigure.x+event.xconfigure.width/10;
2429 windows->widget.y=
2430 event.xconfigure.y+event.xconfigure.height/10;
2431 XConstrainWindowPosition(display,&windows->widget);
2432 window_changes.x=windows->widget.x;
2433 window_changes.y=windows->widget.y;
2434 (void) XReconfigureWMWindow(display,windows->widget.id,
2435 windows->widget.screen,(unsigned int) (CWX | CWY),
2436 &window_changes);
2437 }
2438 }
2439 /*
2440 Image window has a new configuration.
2441 */
2442 windows->image.width=(unsigned int) event.xconfigure.width;
2443 windows->image.height=(unsigned int) event.xconfigure.height;
2444 break;
2445 }
2446 if (event.xconfigure.window == windows->icon.id)
2447 {
2448 /*
2449 Icon window has a new configuration.
2450 */
2451 windows->icon.width=(unsigned int) event.xconfigure.width;
2452 windows->icon.height=(unsigned int) event.xconfigure.height;
2453 break;
2454 }
2455 break;
2456 }
2457 case DestroyNotify:
2458 {
2459 /*
2460 Group leader has exited.
2461 */
2462 if (display_image->debug != MagickFalse)
2463 (void) LogMagickEvent(X11Event,GetMagickModule(),
2464 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
2465 if (event.xdestroywindow.window == windows->group_leader.id)
2466 {
2467 state|=ExitState;
2468 break;
2469 }
2470 break;
2471 }
2472 case EnterNotify:
2473 {
2474 /*
2475 Selectively install colormap.
2476 */
2477 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
2478 if (event.xcrossing.mode != NotifyUngrab)
2479 XInstallColormap(display,map_info->colormap);
2480 break;
2481 }
2482 case Expose:
2483 {
2484 if (display_image->debug != MagickFalse)
2485 (void) LogMagickEvent(X11Event,GetMagickModule(),
2486 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
2487 event.xexpose.width,event.xexpose.height,event.xexpose.x,
2488 event.xexpose.y);
2489 /*
2490 Repaint windows that are now exposed.
2491 */
2492 if (event.xexpose.window == windows->image.id)
2493 {
2494 windows->image.pixmap=windows->image.pixmaps[scene];
2495 windows->image.matte_pixmap=windows->image.matte_pixmaps[scene];
2496 XRefreshWindow(display,&windows->image,&event);
2497 break;
2498 }
2499 if (event.xexpose.window == windows->icon.id)
2500 if (event.xexpose.count == 0)
2501 {
2502 XRefreshWindow(display,&windows->icon,&event);
2503 break;
2504 }
2505 break;
2506 }
2507 case KeyPress:
2508 {
2509 static int
2510 length;
2511
2512 /*
2513 Respond to a user key press.
2514 */
2515 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2516 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2517 *(command+length)='\0';
2518 if (display_image->debug != MagickFalse)
2519 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002520 "Key press: 0x%lx (%c)",(unsigned long) key_symbol,*command);
cristy3ed852e2009-09-05 21:47:34 +00002521 command_type=NullCommand;
2522 switch (key_symbol)
2523 {
2524 case XK_o:
2525 {
2526 if ((event.xkey.state & ControlMask) == MagickFalse)
2527 break;
2528 command_type=OpenCommand;
2529 break;
2530 }
2531 case XK_BackSpace:
2532 {
2533 command_type=StepBackwardCommand;
2534 break;
2535 }
2536 case XK_space:
2537 {
2538 command_type=StepForwardCommand;
2539 break;
2540 }
2541 case XK_less:
2542 {
2543 command_type=FasterCommand;
2544 break;
2545 }
2546 case XK_greater:
2547 {
2548 command_type=SlowerCommand;
2549 break;
2550 }
2551 case XK_F1:
2552 {
2553 command_type=HelpCommand;
2554 break;
2555 }
2556 case XK_Find:
2557 {
2558 command_type=BrowseDocumentationCommand;
2559 break;
2560 }
2561 case XK_question:
2562 {
2563 command_type=InfoCommand;
2564 break;
2565 }
2566 case XK_q:
2567 case XK_Escape:
2568 {
2569 command_type=QuitCommand;
2570 break;
2571 }
2572 default:
2573 break;
2574 }
2575 if (command_type != NullCommand)
2576 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00002577 command_type,&image,&state,exception);
cristy3ed852e2009-09-05 21:47:34 +00002578 break;
2579 }
2580 case KeyRelease:
2581 {
2582 /*
2583 Respond to a user key release.
2584 */
2585 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2586 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2587 if (display_image->debug != MagickFalse)
2588 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002589 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
cristy3ed852e2009-09-05 21:47:34 +00002590 break;
2591 }
2592 case LeaveNotify:
2593 {
2594 /*
2595 Selectively uninstall colormap.
2596 */
2597 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
2598 if (event.xcrossing.mode != NotifyUngrab)
2599 XUninstallColormap(display,map_info->colormap);
2600 break;
2601 }
2602 case MapNotify:
2603 {
2604 if (display_image->debug != MagickFalse)
2605 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
2606 event.xmap.window);
2607 if (event.xmap.window == windows->backdrop.id)
2608 {
2609 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
2610 CurrentTime);
2611 windows->backdrop.mapped=MagickTrue;
2612 break;
2613 }
2614 if (event.xmap.window == windows->image.id)
2615 {
2616 if (windows->backdrop.id != (Window) NULL)
2617 (void) XInstallColormap(display,map_info->colormap);
2618 if (LocaleCompare(image_list[0]->magick,"LOGO") == 0)
2619 {
2620 if (LocaleCompare(display_image->filename,"LOGO") == 0)
2621 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00002622 OpenCommand,&image,&state,exception);
cristy3ed852e2009-09-05 21:47:34 +00002623 else
2624 state|=ExitState;
2625 }
2626 windows->image.mapped=MagickTrue;
2627 break;
2628 }
2629 if (event.xmap.window == windows->info.id)
2630 {
2631 windows->info.mapped=MagickTrue;
2632 break;
2633 }
2634 if (event.xmap.window == windows->icon.id)
2635 {
2636 /*
2637 Create an icon image.
2638 */
2639 XMakeStandardColormap(display,icon_visual,icon_resources,
cristy6710d842011-10-20 23:23:00 +00002640 display_image,icon_map,icon_pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00002641 (void) XMakeImage(display,icon_resources,&windows->icon,
cristy051718b2011-08-28 22:49:25 +00002642 display_image,windows->icon.width,windows->icon.height,
2643 exception);
cristy3ed852e2009-09-05 21:47:34 +00002644 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
2645 windows->icon.pixmap);
2646 (void) XClearWindow(display,windows->icon.id);
2647 (void) XWithdrawWindow(display,windows->info.id,
2648 windows->info.screen);
2649 windows->icon.mapped=MagickTrue;
2650 break;
2651 }
2652 if (event.xmap.window == windows->command.id)
2653 {
2654 windows->command.mapped=MagickTrue;
2655 break;
2656 }
2657 if (event.xmap.window == windows->popup.id)
2658 {
2659 windows->popup.mapped=MagickTrue;
2660 break;
2661 }
2662 if (event.xmap.window == windows->widget.id)
2663 {
2664 windows->widget.mapped=MagickTrue;
2665 break;
2666 }
2667 break;
2668 }
2669 case MappingNotify:
2670 {
2671 (void) XRefreshKeyboardMapping(&event.xmapping);
2672 break;
2673 }
2674 case NoExpose:
2675 break;
2676 case PropertyNotify:
2677 {
2678 Atom
2679 type;
2680
2681 int
2682 format,
2683 status;
2684
2685 unsigned char
2686 *data;
2687
cristyf2faecf2010-05-28 19:19:36 +00002688 unsigned long
cristy3ed852e2009-09-05 21:47:34 +00002689 after,
2690 length;
2691
2692 if (display_image->debug != MagickFalse)
2693 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002694 "Property Notify: 0x%lx 0x%lx %d",(unsigned long)
2695 event.xproperty.window,(unsigned long) event.xproperty.atom,
2696 event.xproperty.state);
cristy3ed852e2009-09-05 21:47:34 +00002697 if (event.xproperty.atom != windows->im_remote_command)
2698 break;
2699 /*
2700 Display image named by the remote command protocol.
2701 */
2702 status=XGetWindowProperty(display,event.xproperty.window,
cristycee97112010-05-28 00:44:52 +00002703 event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
cristy3ed852e2009-09-05 21:47:34 +00002704 AnyPropertyType,&type,&format,&length,&after,&data);
2705 if ((status != Success) || (length == 0))
2706 break;
2707 (void) CopyMagickString(resource_info->image_info->filename,
2708 (char *) data,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00002709 nexus=ReadImage(resource_info->image_info,exception);
2710 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002711 if (nexus != (Image *) NULL)
2712 state|=ExitState;
2713 (void) XFree((void *) data);
2714 break;
2715 }
2716 case ReparentNotify:
2717 {
2718 if (display_image->debug != MagickFalse)
2719 (void) LogMagickEvent(X11Event,GetMagickModule(),
2720 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
2721 event.xreparent.window);
2722 break;
2723 }
2724 case UnmapNotify:
2725 {
2726 if (display_image->debug != MagickFalse)
2727 (void) LogMagickEvent(X11Event,GetMagickModule(),
2728 "Unmap Notify: 0x%lx",event.xunmap.window);
2729 if (event.xunmap.window == windows->backdrop.id)
2730 {
2731 windows->backdrop.mapped=MagickFalse;
2732 break;
2733 }
2734 if (event.xunmap.window == windows->image.id)
2735 {
2736 windows->image.mapped=MagickFalse;
2737 break;
2738 }
2739 if (event.xunmap.window == windows->info.id)
2740 {
2741 windows->info.mapped=MagickFalse;
2742 break;
2743 }
2744 if (event.xunmap.window == windows->icon.id)
2745 {
2746 if (map_info->colormap == icon_map->colormap)
2747 XConfigureImageColormap(display,resource_info,windows,
cristy6710d842011-10-20 23:23:00 +00002748 display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002749 (void) XFreeStandardColormap(display,icon_visual,icon_map,
2750 icon_pixel);
2751 windows->icon.mapped=MagickFalse;
2752 break;
2753 }
2754 if (event.xunmap.window == windows->command.id)
2755 {
2756 windows->command.mapped=MagickFalse;
2757 break;
2758 }
2759 if (event.xunmap.window == windows->popup.id)
2760 {
2761 if (windows->backdrop.id != (Window) NULL)
2762 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
2763 CurrentTime);
2764 windows->popup.mapped=MagickFalse;
2765 break;
2766 }
2767 if (event.xunmap.window == windows->widget.id)
2768 {
2769 if (windows->backdrop.id != (Window) NULL)
2770 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
2771 CurrentTime);
2772 windows->widget.mapped=MagickFalse;
2773 break;
2774 }
2775 break;
2776 }
2777 default:
2778 {
2779 if (display_image->debug != MagickFalse)
2780 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
2781 event.type);
2782 break;
2783 }
2784 }
2785 }
2786 while (!(state & ExitState));
2787 image_list=(Image **) RelinquishMagickMemory(image_list);
2788 images=DestroyImageList(images);
2789 if ((windows->visual_info->klass == GrayScale) ||
2790 (windows->visual_info->klass == PseudoColor) ||
2791 (windows->visual_info->klass == DirectColor))
2792 {
2793 /*
2794 Withdraw windows.
2795 */
2796 if (windows->info.mapped)
2797 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2798 if (windows->command.mapped)
2799 (void) XWithdrawWindow(display,windows->command.id,
2800 windows->command.screen);
2801 }
2802 if (resource_info->backdrop == MagickFalse)
2803 if (windows->backdrop.mapped)
2804 {
2805 (void) XWithdrawWindow(display,windows->backdrop.id,\
2806 windows->backdrop.screen);
2807 (void) XDestroyWindow(display,windows->backdrop.id);
2808 windows->backdrop.id=(Window) NULL;
2809 (void) XWithdrawWindow(display,windows->image.id,windows->image.screen);
2810 (void) XDestroyWindow(display,windows->image.id);
2811 windows->image.id=(Window) NULL;
2812 }
2813 XSetCursorState(display,windows,MagickTrue);
2814 XCheckRefreshWindows(display,windows);
cristybb503372010-05-27 20:51:26 +00002815 for (scene=1; scene < (ssize_t) number_scenes; scene++)
cristy3ed852e2009-09-05 21:47:34 +00002816 {
2817 if (windows->image.pixmaps[scene] != (Pixmap) NULL)
2818 (void) XFreePixmap(display,windows->image.pixmaps[scene]);
2819 windows->image.pixmaps[scene]=(Pixmap) NULL;
2820 if (windows->image.matte_pixmaps[scene] != (Pixmap) NULL)
2821 (void) XFreePixmap(display,windows->image.matte_pixmaps[scene]);
2822 windows->image.matte_pixmaps[scene]=(Pixmap) NULL;
2823 }
cristy04e11fd2010-03-19 16:01:44 +00002824 XSetCursorState(display,windows,MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00002825 windows->image.pixmaps=(Pixmap *)
2826 RelinquishMagickMemory(windows->image.pixmaps);
2827 windows->image.matte_pixmaps=(Pixmap *)
2828 RelinquishMagickMemory(windows->image.matte_pixmaps);
2829 if (nexus == (Image *) NULL)
2830 {
2831 /*
2832 Free X resources.
2833 */
2834 if (windows->image.mapped != MagickFalse)
cristy8418c7e2014-05-18 18:38:26 +00002835 (void) XWithdrawWindow(display,windows->image.id,windows->image.screen);
2836 XDelay(display,SuspendTime);
cristy3ed852e2009-09-05 21:47:34 +00002837 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
2838 if (resource_info->map_type == (char *) NULL)
2839 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
2840 DestroyXResources();
2841 }
2842 (void) XSync(display,MagickFalse);
2843 /*
2844 Restore our progress monitor and warning handlers.
2845 */
2846 (void) SetErrorHandler(warning_handler);
2847 (void) SetWarningHandler(warning_handler);
2848 /*
2849 Change to home directory.
2850 */
cristy00976d82011-02-20 20:31:28 +00002851 directory=getcwd(working_directory,MaxTextExtent);
2852 (void) directory;
cristy8a5d7f42013-01-06 15:24:33 +00002853 if (*resource_info->home_directory == '\0')
2854 (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00002855 status=chdir(resource_info->home_directory);
2856 if (status == -1)
cristy051718b2011-08-28 22:49:25 +00002857 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
2858 "UnableToOpenFile","%s",resource_info->home_directory);
cristy3ed852e2009-09-05 21:47:34 +00002859 return(nexus);
2860}
2861
2862/*
2863%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2864% %
2865% %
2866% %
2867+ X S a v e I m a g e %
2868% %
2869% %
2870% %
2871%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2872%
2873% XSaveImage() saves an image to a file.
2874%
2875% The format of the XSaveImage method is:
2876%
2877% MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002878% XResourceInfo *resource_info,XWindows *windows,Image *image,
2879% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002880%
2881% A description of each parameter follows:
2882%
2883% o status: Method XSaveImage return True if the image is
2884% written. False is returned is there is a memory shortage or if the
2885% image fails to write.
2886%
2887% o display: Specifies a connection to an X server; returned from
2888% XOpenDisplay.
2889%
2890% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2891%
2892% o windows: Specifies a pointer to a XWindows structure.
2893%
2894% o image: the image.
2895%
2896*/
2897static MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002898 XResourceInfo *resource_info,XWindows *windows,Image *image,
2899 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002900{
2901 char
2902 filename[MaxTextExtent];
2903
2904 ImageInfo
2905 *image_info;
2906
2907 MagickStatusType
2908 status;
2909
2910 /*
2911 Request file name from user.
2912 */
2913 if (resource_info->write_filename != (char *) NULL)
2914 (void) CopyMagickString(filename,resource_info->write_filename,
2915 MaxTextExtent);
2916 else
2917 {
2918 char
2919 path[MaxTextExtent];
2920
2921 int
2922 status;
2923
2924 GetPathComponent(image->filename,HeadPath,path);
2925 GetPathComponent(image->filename,TailPath,filename);
cristy8a5d7f42013-01-06 15:24:33 +00002926 if (*path == '\0')
2927 (void) CopyMagickString(path,".",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00002928 status=chdir(path);
2929 if (status == -1)
cristy051718b2011-08-28 22:49:25 +00002930 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
2931 "UnableToOpenFile","%s",path);
cristy3ed852e2009-09-05 21:47:34 +00002932 }
2933 XFileBrowserWidget(display,windows,"Save",filename);
2934 if (*filename == '\0')
2935 return(MagickTrue);
2936 if (IsPathAccessible(filename) != MagickFalse)
2937 {
2938 int
2939 status;
2940
2941 /*
2942 File exists-- seek user's permission before overwriting.
2943 */
2944 status=XConfirmWidget(display,windows,"Overwrite",filename);
2945 if (status == 0)
2946 return(MagickTrue);
2947 }
2948 image_info=CloneImageInfo(resource_info->image_info);
2949 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00002950 (void) SetImageInfo(image_info,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00002951 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
2952 (LocaleCompare(image_info->magick,"JPG") == 0))
2953 {
2954 char
2955 quality[MaxTextExtent];
2956
2957 int
2958 status;
2959
2960 /*
2961 Request JPEG quality from user.
2962 */
cristyb51dff52011-05-19 16:55:47 +00002963 (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002964 image_info->quality);
cristy3ed852e2009-09-05 21:47:34 +00002965 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
2966 quality);
2967 if (*quality == '\0')
2968 return(MagickTrue);
cristye27293e2009-12-18 02:53:20 +00002969 image->quality=StringToUnsignedLong(quality);
cristy3ed852e2009-09-05 21:47:34 +00002970 image_info->interlace=status != MagickFalse ? NoInterlace :
2971 PlaneInterlace;
2972 }
2973 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
2974 (LocaleCompare(image_info->magick,"PDF") == 0) ||
2975 (LocaleCompare(image_info->magick,"PS") == 0) ||
2976 (LocaleCompare(image_info->magick,"PS2") == 0))
2977 {
2978 char
2979 geometry[MaxTextExtent];
2980
2981 /*
2982 Request page geometry from user.
2983 */
cristy42c2d972009-09-13 01:12:39 +00002984 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00002985 if (LocaleCompare(image_info->magick,"PDF") == 0)
cristy42c2d972009-09-13 01:12:39 +00002986 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00002987 if (image_info->page != (char *) NULL)
2988 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
2989 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
2990 "Select page geometry:",geometry);
2991 if (*geometry != '\0')
2992 image_info->page=GetPageGeometry(geometry);
2993 }
2994 /*
2995 Write image.
2996 */
2997 image=GetFirstImageInList(image);
cristy051718b2011-08-28 22:49:25 +00002998 status=WriteImages(image_info,image,filename,exception);
cristy3ed852e2009-09-05 21:47:34 +00002999 if (status != MagickFalse)
3000 image->taint=MagickFalse;
3001 image_info=DestroyImageInfo(image_info);
3002 XSetCursorState(display,windows,MagickFalse);
3003 return(status != 0 ? MagickTrue : MagickFalse);
3004}
3005#else
3006
3007/*
3008%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3009% %
3010% %
3011% %
3012+ A n i m a t e I m a g e s %
3013% %
3014% %
3015% %
3016%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3017%
3018% AnimateImages() repeatedly displays an image sequence to any X window
3019% screen. It returns a value other than 0 if successful. Check the
3020% exception member of image to determine the reason for any failure.
3021%
3022% The format of the AnimateImages method is:
3023%
3024% MagickBooleanType AnimateImages(const ImageInfo *image_info,
3025% Image *images)
3026%
3027% A description of each parameter follows:
3028%
3029% o image_info: the image info.
3030%
3031% o image: the image.
3032%
cristy051718b2011-08-28 22:49:25 +00003033% o exception: return any errors or warnings in this structure.
3034%
cristy3ed852e2009-09-05 21:47:34 +00003035*/
3036MagickExport MagickBooleanType AnimateImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +00003037 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003038{
3039 assert(image_info != (const ImageInfo *) NULL);
3040 assert(image_info->signature == MagickSignature);
cristy4f88ff72013-12-08 17:16:11 +00003041 (void) image_info;
cristy3ed852e2009-09-05 21:47:34 +00003042 assert(image != (Image *) NULL);
3043 assert(image->signature == MagickSignature);
3044 if (image->debug != MagickFalse)
3045 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy051718b2011-08-28 22:49:25 +00003046 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
anthonye5b39652012-04-21 05:37:29 +00003047 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00003048 return(MagickFalse);
3049}
3050#endif