]> granicus.if.org Git - imagemagick/blob - MagickCore/channel.c
(no commit message)
[imagemagick] / MagickCore / channel.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               CCCC  H   H   AAA   N   N  N   N  EEEEE   L                   %
7 %              C      H   H  A   A  NN  N  NN  N  E       L                   %
8 %              C      HHHHH  AAAAA  N N N  N N N  RRR     L                   %
9 %              C      H   H  A   A  N  NN  N  NN  E       L                   %
10 %               CCCC  H   H  A   A  N   N  N   N  EEEEE   LLLLL               %
11 %                                                                             %
12 %                                                                             %
13 %                      MagickCore Image Channel Methods                       %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                               December 2003                                 %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    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 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/image.h"
45 #include "MagickCore/list.h"
46 #include "MagickCore/log.h"
47 #include "MagickCore/monitor.h"
48 #include "MagickCore/monitor-private.h"
49 #include "MagickCore/option.h"
50 #include "MagickCore/pixel-accessor.h"
51 #include "MagickCore/string-private.h"
52 #include "MagickCore/token.h"
53 #include "MagickCore/utility.h"
54 #include "MagickCore/version.h"
55 \f
56 /*
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58 %                                                                             %
59 %                                                                             %
60 %                                                                             %
61 %     C h a n n e l F x I m a g e                                             %
62 %                                                                             %
63 %                                                                             %
64 %                                                                             %
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 %
67 %  ChannelFxImage() applies a channel expression to the specified image.  The
68 %  expression consists of one or more channels, either mnemonic or numeric (e.g.
69 %  red, 1), separated by actions as follows:
70 %
71 %    <=>     exchange two channels (e.g. red<=>blue)
72 %    =>      transfer a channel to another (e.g. red=>green)
73 %    =       assign a constant (e.g. red=50%)
74 %    ,       separate channel operations (e.g. red, green)
75 %    |       read channels from next input image (e.g. red | green)
76 %    ;       write channels to next output image (e.g. red; green; blue)
77 %
78 %  A channel without a operation symbol implies extract. For example, to create
79 %  3 grayscale images from the red, green, and blue channels of an image, use:
80 %
81 %    -channel-fx "red; green; blue"
82 %
83 %  The format of the ChannelFxImage method is:
84 %
85 %      Image *ChannelFxImage(const Image *image,const char *expression,
86 %        ExceptionInfo *exception)
87 %
88 %  A description of each parameter follows:
89 %
90 %    o image: the image.
91 %
92 %    o expression: A channel expression.
93 %
94 %    o exception: return any errors or warnings in this structure.
95 %
96 */
97
98 typedef enum
99 {
100   ExtractChannelOp,
101   AssignChannelOp,
102   ExchangeChannelOp,
103   TransferChannelOp
104 } ChannelFx;
105
106 static inline size_t MagickMin(const size_t x,const size_t y)
107 {
108   if (x < y)
109     return(x);
110   return(y);
111 }
112
113 static MagickBooleanType ChannelImage(Image *destination_image,
114   const Image *source_image,const PixelChannel source_channel,
115   const PixelChannel destination_channel,const ChannelFx channel_op,
116   const Quantum pixel,ExceptionInfo *exception)
117 {
118   CacheView
119     *source_view,
120     *destination_view;
121
122   MagickBooleanType
123     status;
124
125   size_t
126     height;
127
128   ssize_t
129     y;
130
131   status=MagickTrue;
132   source_view=AcquireCacheView(source_image);
133   destination_view=AcquireCacheView(destination_image);
134   height=MagickMin(source_image->rows,destination_image->rows);
135 #if defined(MAGICKCORE_OPENMP_SUPPORT)
136   #pragma omp parallel for schedule(static) shared(status)
137 #endif
138   for (y=0; y < (ssize_t) height; y++)
139   {
140     register const Quantum
141       *restrict p;
142
143     register Quantum
144       *restrict q;
145
146     register ssize_t
147       x;
148
149     size_t
150       width;
151
152     if (status == MagickFalse)
153       continue;
154     p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
155       exception);
156     q=QueueCacheViewAuthenticPixels(destination_view,0,y,
157       destination_image->columns,1,exception);
158     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
159       {
160         status=MagickFalse;
161         continue;
162       }
163     width=MagickMin(source_image->columns,destination_image->columns);
164     for (x=0; x < (ssize_t) width; x++)
165     {
166       PixelTrait
167         destination_traits,
168         source_traits;
169
170       ssize_t
171         offset;
172
173       source_traits=GetPixelChannelMapTraits(source_image,source_channel);
174       destination_traits=GetPixelChannelMapTraits(destination_image,
175         destination_channel);
176       if ((source_traits == UndefinedPixelTrait) ||
177           (destination_traits == UndefinedPixelTrait))
178         continue;
179       offset=GetPixelChannelMapOffset(source_image,source_channel);
180       if (channel_op == AssignChannelOp)
181         SetPixelChannel(destination_image,destination_channel,pixel,q);
182       else
183         SetPixelChannel(destination_image,destination_channel,p[offset],q);
184       p+=GetPixelChannels(source_image);
185       q+=GetPixelChannels(destination_image);
186     }
187     if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
188       status=MagickFalse;
189   }
190   destination_view=DestroyCacheView(destination_view);
191   source_view=DestroyCacheView(source_view);
192   return(status);
193 }
194
195 MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
196   ExceptionInfo *exception)
197 {
198 #define ChannelFxImageTag  "ChannelFx/Image"
199
200   char
201     token[MaxTextExtent];
202
203   ChannelFx
204     channel_op;
205
206   const char
207     *p;
208
209   const Image
210     *source_image;
211
212   double
213     pixel;
214
215   Image
216     *destination_image;
217
218   PixelChannel
219     source_channel,
220     destination_channel;
221
222   ssize_t
223     channels;
224
225   assert(image != (Image *) NULL);
226   assert(image->signature == MagickSignature);
227   if (image->debug != MagickFalse)
228     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
229   assert(exception != (ExceptionInfo *) NULL);
230   assert(exception->signature == MagickSignature);
231   source_image=image;
232   destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
233   if (destination_image == (Image *) NULL)
234     return((Image *) NULL);
235   if (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
236     {
237       destination_image=GetLastImageInList(destination_image);
238       return((Image *) NULL);
239     }
240   if (expression == (const char *) NULL)
241     return(destination_image);
242   destination_channel=RedPixelChannel;
243   pixel=0.0;
244   p=(char *) expression;
245   GetMagickToken(p,&p,token);
246   for (channels=0; *p != '\0'; )
247   {
248     MagickBooleanType
249       status;
250
251     ssize_t
252       i;
253
254     /*
255       Interpret channel expression.
256     */
257     if (*token == ',')
258       {
259         destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
260         GetMagickToken(p,&p,token);
261       }
262     if (*token == '|')
263       {
264         if (GetNextImageInList(source_image) != (Image *) NULL)
265           source_image=GetNextImageInList(source_image);
266         else
267           source_image=GetFirstImageInList(source_image);
268         GetMagickToken(p,&p,token);
269       }
270     if (*token == ';')
271       {
272         Image
273           *canvas;
274
275         if (channels == 1)
276           {
277             destination_image->colorspace=GRAYColorspace;
278             InitializePixelChannelMap(destination_image);
279           }
280         canvas=CloneImage(source_image,0,0,MagickTrue,exception);
281         if (canvas == (Image *) NULL)
282           {
283             destination_image=GetLastImageInList(destination_image);
284             return((Image *) NULL);
285           }
286         AppendImageToList(&destination_image,canvas);
287         destination_image=GetLastImageInList(destination_image);
288         if (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
289           {
290             destination_image=GetLastImageInList(destination_image);
291             return((Image *) NULL);
292           }
293         GetMagickToken(p,&p,token);
294         channels=0;
295         destination_channel=RedPixelChannel;
296       }
297     i=ParsePixelChannelOption(token);
298     if (i < 0)
299       {
300         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
301           "UnrecognizedChannelType","`%s'",token);
302         destination_image=DestroyImageList(destination_image);
303         break;
304       }
305     source_channel=(PixelChannel) i;
306     channel_op=ExtractChannelOp;
307     GetMagickToken(p,&p,token);
308     if (*token == '<')
309       {
310         channel_op=ExchangeChannelOp;
311         GetMagickToken(p,&p,token);
312       }
313     if (*token == '=')
314       {
315         if (channel_op != ExchangeChannelOp)
316           channel_op=AssignChannelOp;
317         GetMagickToken(p,&p,token);
318       }
319     if (*token == '>')
320       {
321         if (channel_op != ExchangeChannelOp)
322           channel_op=TransferChannelOp;
323         GetMagickToken(p,&p,token);
324       }
325     switch (channel_op)
326     {
327       case AssignChannelOp:
328       {
329         pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
330         break;
331       }
332       case ExchangeChannelOp:
333       case TransferChannelOp:
334       {
335         i=ParsePixelChannelOption(token);
336         if (i < 0)
337           {
338             (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
339               "UnrecognizedChannelType","`%s'",token);
340             destination_image=DestroyImageList(destination_image);
341             break;
342           }
343         destination_channel=(PixelChannel) i;
344         break;
345       }
346       case ExtractChannelOp:
347         break;
348      }
349     status=ChannelImage(destination_image,source_image,source_channel,
350       destination_channel,channel_op,ClampToQuantum(pixel),exception);
351     if (status == MagickFalse)
352       {
353         destination_image=DestroyImageList(destination_image);
354         break;
355       }
356     if (channel_op == ExchangeChannelOp)
357       {
358         status=ChannelImage(destination_image,source_image,destination_channel,
359           source_channel,channel_op,ClampToQuantum(pixel),exception);
360         if (status == MagickFalse)
361           {
362             destination_image=DestroyImageList(destination_image);
363             break;
364           }
365       }
366     channels++;
367     status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
368       strlen(expression));
369     if (status == MagickFalse)
370       break;
371   }
372   if (channels == 1)
373     {
374       destination_image->colorspace=GRAYColorspace;
375       InitializePixelChannelMap(destination_image);
376     }
377   return(destination_image);
378 }
379 \f
380 /*
381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
382 %                                                                             %
383 %                                                                             %
384 %                                                                             %
385 %     C o m b i n e I m a g e s                                               %
386 %                                                                             %
387 %                                                                             %
388 %                                                                             %
389 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
390 %
391 %  CombineImages() combines one or more images into a single image.  The
392 %  grayscale value of the pixels of each image in the sequence is assigned in
393 %  order to the specified channels of the combined image.   The typical
394 %  ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
395 %
396 %  The format of the CombineImages method is:
397 %
398 %      Image *CombineImages(const Image *image,ExceptionInfo *exception)
399 %
400 %  A description of each parameter follows:
401 %
402 %    o image: the image.
403 %
404 %    o exception: return any errors or warnings in this structure.
405 %
406 */
407 MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
408 {
409 #define CombineImageTag  "Combine/Image"
410
411   CacheView
412     *combine_view;
413
414   Image
415     *combine_image;
416
417   MagickBooleanType
418     status;
419
420   MagickOffsetType
421     progress;
422
423   ssize_t
424     y;
425
426   /*
427     Ensure the image are the same size.
428   */
429   assert(image != (const Image *) NULL);
430   assert(image->signature == MagickSignature);
431   if (image->debug != MagickFalse)
432     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
433   assert(exception != (ExceptionInfo *) NULL);
434   assert(exception->signature == MagickSignature);
435   combine_image=CloneImage(image,0,0,MagickTrue,exception);
436   if (combine_image == (Image *) NULL)
437     return((Image *) NULL);
438   if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
439     {
440       combine_image=DestroyImage(combine_image);
441       return((Image *) NULL);
442     }
443   if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
444     combine_image->matte=MagickTrue;
445   /*
446     Combine images.
447   */
448   status=MagickTrue;
449   progress=0;
450   combine_view=AcquireCacheView(combine_image);
451   for (y=0; y < (ssize_t) combine_image->rows; y++)
452   {
453     CacheView
454       *image_view;
455
456     const Image
457       *next;
458
459     Quantum
460       *pixels;
461
462     register const Quantum
463       *restrict p;
464
465     register Quantum
466       *restrict q;
467
468     register ssize_t
469       i;
470
471     if (status == MagickFalse)
472       continue;
473     pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
474       1,exception);
475     if (pixels == (Quantum *) NULL)
476       {
477         status=MagickFalse;
478         continue;
479       }
480     next=image;
481     for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
482     {
483       PixelChannel
484         channel;
485
486       PixelTrait
487         combine_traits,
488         traits;
489
490       register ssize_t
491         x;
492
493       if (next == (Image *) NULL)
494         continue;
495       channel=GetPixelChannelMapChannel(image,i);
496       traits=GetPixelChannelMapTraits(image,channel);
497       combine_traits=GetPixelChannelMapTraits(combine_image,channel);
498       if ((traits == UndefinedPixelTrait) ||
499           (combine_traits == UndefinedPixelTrait))
500         continue;
501       image_view=AcquireCacheView(next);
502       p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
503       if (p == (const Quantum *) NULL)
504         continue;
505       q=pixels;
506       for (x=0; x < (ssize_t) combine_image->columns; x++)
507       {
508         if (x < (ssize_t) image->columns)
509           {
510             q[i]=GetPixelGray(image,p);
511             p+=GetPixelChannels(image);
512           }
513         q+=GetPixelChannels(combine_image);
514       }
515       image_view=DestroyCacheView(image_view);
516       next=GetNextImageInList(next);
517       if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
518         status=MagickFalse;
519       if (image->progress_monitor != (MagickProgressMonitor) NULL)
520         {
521           MagickBooleanType
522             proceed;
523
524           proceed=SetImageProgress(image,CombineImageTag,progress++,
525             combine_image->rows);
526           if (proceed == MagickFalse)
527             status=MagickFalse;
528         }
529     }
530   }
531   combine_view=DestroyCacheView(combine_view);
532   if (status == MagickFalse)
533     combine_image=DestroyImage(combine_image);
534   return(combine_image);
535 }
536 \f
537 /*
538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
539 %                                                                             %
540 %                                                                             %
541 %                                                                             %
542 %     S e p a r a t e I m a g e                                               %
543 %                                                                             %
544 %                                                                             %
545 %                                                                             %
546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547 %
548 %  SeparateImage() separates a channel from the image and returns it as a
549 %  grayscale image.
550 %
551 %  The format of the SeparateImage method is:
552 %
553 %      Image *SeparateImage(const Image *image,const ChannelType channel,
554 %        ExceptionInfo *exception)
555 %
556 %  A description of each parameter follows:
557 %
558 %    o image: the image.
559 %
560 %    o channel: the image channel.
561 %
562 %    o exception: return any errors or warnings in this structure.
563 %
564 */
565 MagickExport Image *SeparateImage(const Image *image,
566   const ChannelType channel_type,ExceptionInfo *exception)
567 {
568 #define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
569 #define SeparateImageTag  "Separate/Image"
570
571   CacheView
572     *image_view,
573     *separate_view;
574
575   Image
576     *separate_image;
577
578   MagickBooleanType
579     status;
580
581   MagickOffsetType
582     progress;
583
584   ssize_t
585     y;
586
587   /*
588     Initialize spread image attributes.
589   */
590   assert(image != (Image *) NULL);
591   assert(image->signature == MagickSignature);
592   if (image->debug != MagickFalse)
593     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
594   assert(exception != (ExceptionInfo *) NULL);
595   assert(exception->signature == MagickSignature);
596   separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
597     exception);
598   if (separate_image == (Image *) NULL)
599     return((Image *) NULL);
600   if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
601     {
602       separate_image=DestroyImage(separate_image);
603       return((Image *) NULL);
604     }
605   separate_image->colorspace=GRAYColorspace;
606   /*
607     Separate image.
608   */
609   status=MagickTrue;
610   progress=0;
611   image_view=AcquireCacheView(image);
612   separate_view=AcquireCacheView(separate_image);
613 #if defined(MAGICKCORE_OPENMP_SUPPORT)
614   #pragma omp parallel for schedule(static) shared(progress,status)
615 #endif
616   for (y=0; y < (ssize_t) image->rows; y++)
617   {
618     register const Quantum
619       *restrict p;
620
621     register Quantum
622       *restrict q;
623
624     register ssize_t
625       x;
626
627     if (status == MagickFalse)
628       continue;
629     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
630     q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
631       exception);
632     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
633       {
634         status=MagickFalse;
635         continue;
636       }
637     for (x=0; x < (ssize_t) image->columns; x++)
638     {
639       register ssize_t
640         i;
641
642       if (GetPixelMask(image,p) != 0)
643         {
644           p+=GetPixelChannels(image);
645           q+=GetPixelChannels(separate_image);
646           continue;
647         }
648       SetPixelChannel(separate_image,GrayPixelChannel,0,q);
649       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
650       {
651         PixelChannel
652           channel;
653
654         PixelTrait
655           traits;
656
657         channel=GetPixelChannelMapChannel(image,i);
658         traits=GetPixelChannelMapTraits(image,channel);
659         if ((traits == UndefinedPixelTrait) ||
660             (GetChannelBit(channel_type,channel) == 0))
661           continue;
662         SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
663       }
664       p+=GetPixelChannels(image);
665       q+=GetPixelChannels(separate_image);
666     }
667     if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
668       status=MagickFalse;
669     if (image->progress_monitor != (MagickProgressMonitor) NULL)
670       {
671         MagickBooleanType
672           proceed;
673
674 #if defined(MAGICKCORE_OPENMP_SUPPORT)
675   #pragma omp critical (MagickCore_SeparateImage)
676 #endif
677         proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
678         if (proceed == MagickFalse)
679           status=MagickFalse;
680       }
681   }
682   separate_view=DestroyCacheView(separate_view);
683   image_view=DestroyCacheView(image_view);
684   return(separate_image);
685 }
686 \f
687 /*
688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
689 %                                                                             %
690 %                                                                             %
691 %                                                                             %
692 %     S e p a r a t e I m a g e s                                             %
693 %                                                                             %
694 %                                                                             %
695 %                                                                             %
696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
697 %
698 %  SeparateImages() returns a separate grayscale image for each channel
699 %  specified.
700 %
701 %  The format of the SeparateImages method is:
702 %
703 %      MagickBooleanType SeparateImages(const Image *image,
704 %        ExceptionInfo *exception)
705 %
706 %  A description of each parameter follows:
707 %
708 %    o image: the image.
709 %
710 %    o exception: return any errors or warnings in this structure.
711 %
712 */
713 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
714 {
715   Image
716     *images,
717     *separate_image;
718
719   register ssize_t
720     i;
721
722   assert(image != (Image *) NULL);
723   assert(image->signature == MagickSignature);
724   if (image->debug != MagickFalse)
725     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
726   images=NewImageList();
727   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
728   {
729     PixelChannel
730       channel;
731
732     PixelTrait
733       traits;
734
735     channel=GetPixelChannelMapChannel(image,i);
736     traits=GetPixelChannelMapTraits(image,channel);
737     if ((traits == UndefinedPixelTrait) ||
738         ((traits & UpdatePixelTrait) == 0))
739       continue;
740     separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
741     if (separate_image != (Image *) NULL)
742       AppendImageToList(&images,separate_image);
743   }
744   return(images);
745 }