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