/src/imagemagick/MagickCore/channel.c
Line | Count | Source |
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 EEE 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 | | % Cristy % |
17 | | % December 2003 % |
18 | | % % |
19 | | % % |
20 | | % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization % |
21 | | % dedicated to making software imaging solutions freely available. % |
22 | | % % |
23 | | % You may not use this file except in compliance with the License. You may % |
24 | | % obtain a copy of the License at % |
25 | | % % |
26 | | % https://imagemagick.org/license/ % |
27 | | % % |
28 | | % Unless required by applicable law or agreed to in writing, software % |
29 | | % distributed under the License is distributed on an "AS IS" BASIS, % |
30 | | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
31 | | % See the License for the specific language governing permissions and % |
32 | | % limitations under the License. % |
33 | | % % |
34 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
35 | | % |
36 | | % |
37 | | % |
38 | | */ |
39 | | |
40 | | /* |
41 | | Include declarations. |
42 | | */ |
43 | | #include "MagickCore/studio.h" |
44 | | #include "MagickCore/cache-private.h" |
45 | | #include "MagickCore/channel.h" |
46 | | #include "MagickCore/colorspace-private.h" |
47 | | #include "MagickCore/composite-private.h" |
48 | | #include "MagickCore/enhance.h" |
49 | | #include "MagickCore/image.h" |
50 | | #include "MagickCore/list.h" |
51 | | #include "MagickCore/log.h" |
52 | | #include "MagickCore/monitor.h" |
53 | | #include "MagickCore/monitor-private.h" |
54 | | #include "MagickCore/option.h" |
55 | | #include "MagickCore/pixel-accessor.h" |
56 | | #include "MagickCore/resource_.h" |
57 | | #include "MagickCore/string-private.h" |
58 | | #include "MagickCore/thread-private.h" |
59 | | #include "MagickCore/token.h" |
60 | | #include "MagickCore/utility.h" |
61 | | #include "MagickCore/version.h" |
62 | | |
63 | | /* |
64 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
65 | | % % |
66 | | % % |
67 | | % % |
68 | | % C h a n n e l F x I m a g e % |
69 | | % % |
70 | | % % |
71 | | % % |
72 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
73 | | % |
74 | | % ChannelFxImage() applies a channel expression to the specified image. The |
75 | | % expression consists of one or more channels, either mnemonic or numeric (e.g. |
76 | | % r, red, 0), separated by actions as follows: |
77 | | % |
78 | | % <=> exchange two channels (e.g. red<=>blue) |
79 | | % => copy one channel to another channel (e.g. red=>green) |
80 | | % = assign a constant value to a channel (e.g. red=50%) |
81 | | % , write new image channels in the specified order (e.g. red, green) |
82 | | % ; add a new output image for the next set of channel operations |
83 | | % | move to the next input image for the source of channel data |
84 | | % If there are no more images in the list, | has no effect. |
85 | | % |
86 | | % For example, to create 3 grayscale images from the red, green, and blue |
87 | | % channels of an image, use: |
88 | | % |
89 | | % -channel-fx "red; green; blue" |
90 | | % |
91 | | % A channel without an operation symbol implies separate (i.e, semicolon). |
92 | | % |
93 | | % The format of the ChannelFxImage method is: |
94 | | % |
95 | | % Image *ChannelFxImage(const Image *image,const char *expression, |
96 | | % ExceptionInfo *exception) |
97 | | % |
98 | | % A description of each parameter follows: |
99 | | % |
100 | | % o image: the image. |
101 | | % |
102 | | % o expression: A channel expression. |
103 | | % |
104 | | % o exception: return any errors or warnings in this structure. |
105 | | % |
106 | | */ |
107 | | |
108 | | typedef enum |
109 | | { |
110 | | ExtractChannelOp, |
111 | | AssignChannelOp, |
112 | | ExchangeChannelOp, |
113 | | TransferChannelOp |
114 | | } ChannelFx; |
115 | | |
116 | | static MagickBooleanType ChannelImage(Image *destination_image, |
117 | | const PixelChannel destination_channel,const ChannelFx channel_op, |
118 | | const Image *source_image,const PixelChannel source_channel, |
119 | | const Quantum pixel,ExceptionInfo *exception) |
120 | 0 | { |
121 | 0 | CacheView |
122 | 0 | *source_view, |
123 | 0 | *destination_view; |
124 | |
|
125 | 0 | MagickBooleanType |
126 | 0 | status = MagickTrue; |
127 | |
|
128 | 0 | size_t |
129 | 0 | height, |
130 | 0 | width; |
131 | |
|
132 | 0 | ssize_t |
133 | 0 | y; |
134 | | |
135 | | /* |
136 | | Copy source channel to destination. |
137 | | */ |
138 | 0 | height=MagickMin(source_image->rows,destination_image->rows); |
139 | 0 | width=MagickMin(source_image->columns,destination_image->columns); |
140 | 0 | source_view=AcquireVirtualCacheView(source_image,exception); |
141 | 0 | destination_view=AcquireAuthenticCacheView(destination_image,exception); |
142 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
143 | | #pragma omp parallel for schedule(static) shared(status) \ |
144 | | magick_number_threads(source_image,source_image,height,4) |
145 | | #endif |
146 | 0 | for (y=0; y < (ssize_t) height; y++) |
147 | 0 | { |
148 | 0 | PixelTrait |
149 | 0 | destination_traits, |
150 | 0 | source_traits; |
151 | |
|
152 | 0 | const Quantum |
153 | 0 | *magick_restrict p; |
154 | |
|
155 | 0 | Quantum |
156 | 0 | *magick_restrict q; |
157 | |
|
158 | 0 | ssize_t |
159 | 0 | x; |
160 | |
|
161 | 0 | if (status == MagickFalse) |
162 | 0 | continue; |
163 | 0 | p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1, |
164 | 0 | exception); |
165 | 0 | q=GetCacheViewAuthenticPixels(destination_view,0,y, |
166 | 0 | destination_image->columns,1,exception); |
167 | 0 | if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) |
168 | 0 | { |
169 | 0 | status=MagickFalse; |
170 | 0 | continue; |
171 | 0 | } |
172 | 0 | destination_traits=GetPixelChannelTraits(destination_image, |
173 | 0 | destination_channel); |
174 | 0 | source_traits=GetPixelChannelTraits(source_image,source_channel); |
175 | 0 | if ((destination_traits == UndefinedPixelTrait) || |
176 | 0 | (source_traits == UndefinedPixelTrait)) |
177 | 0 | continue; |
178 | 0 | for (x=0; x < (ssize_t) width; x++) |
179 | 0 | { |
180 | 0 | if (channel_op == AssignChannelOp) |
181 | 0 | SetPixelChannel(destination_image,destination_channel,pixel,q); |
182 | 0 | else |
183 | 0 | SetPixelChannel(destination_image,destination_channel, |
184 | 0 | GetPixelChannel(source_image,source_channel,p),q); |
185 | 0 | p+=(ptrdiff_t) GetPixelChannels(source_image); |
186 | 0 | q+=(ptrdiff_t) GetPixelChannels(destination_image); |
187 | 0 | } |
188 | 0 | if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse) |
189 | 0 | status=MagickFalse; |
190 | 0 | } |
191 | 0 | destination_view=DestroyCacheView(destination_view); |
192 | 0 | source_view=DestroyCacheView(source_view); |
193 | 0 | return(status); |
194 | 0 | } |
195 | | |
196 | | MagickExport Image *ChannelFxImage(const Image *image,const char *expression, |
197 | | ExceptionInfo *exception) |
198 | 0 | { |
199 | 0 | #define ChannelFxImageTag "ChannelFx/Image" |
200 | |
|
201 | 0 | ChannelFx |
202 | 0 | channel_op = ExtractChannelOp; |
203 | |
|
204 | 0 | ChannelType |
205 | 0 | channel_mask; |
206 | |
|
207 | 0 | char |
208 | 0 | token[MagickPathExtent] = ""; |
209 | |
|
210 | 0 | const char |
211 | 0 | *p; |
212 | |
|
213 | 0 | const Image |
214 | 0 | *source_image; |
215 | |
|
216 | 0 | double |
217 | 0 | pixel = 0.0; |
218 | |
|
219 | 0 | Image |
220 | 0 | *destination_image; |
221 | |
|
222 | 0 | MagickBooleanType |
223 | 0 | status = MagickTrue; |
224 | |
|
225 | 0 | PixelChannel |
226 | 0 | source_channel, |
227 | 0 | destination_channel = RedPixelChannel; |
228 | |
|
229 | 0 | ssize_t |
230 | 0 | channels = 0; |
231 | |
|
232 | 0 | assert(image != (Image *) NULL); |
233 | 0 | assert(image->signature == MagickCoreSignature); |
234 | 0 | if (IsEventLogging() != MagickFalse) |
235 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
236 | 0 | assert(exception != (ExceptionInfo *) NULL); |
237 | 0 | assert(exception->signature == MagickCoreSignature); |
238 | 0 | p=expression; |
239 | 0 | source_image=image; |
240 | 0 | destination_image=CloneImage(image,0,0,MagickTrue,exception); |
241 | 0 | if (destination_image == (Image *) NULL) |
242 | 0 | return((Image *) NULL); |
243 | 0 | if (expression == (const char *) NULL) |
244 | 0 | return(destination_image); |
245 | 0 | status=SetImageStorageClass(destination_image,DirectClass,exception); |
246 | 0 | if (status == MagickFalse) |
247 | 0 | { |
248 | 0 | destination_image=GetLastImageInList(destination_image); |
249 | 0 | return((Image *) NULL); |
250 | 0 | } |
251 | 0 | channel_mask=destination_image->channel_mask; |
252 | 0 | (void) GetNextToken(p,&p,MagickPathExtent,token); |
253 | 0 | while (*token != '\0') |
254 | 0 | { |
255 | 0 | PixelTrait |
256 | 0 | traits; |
257 | |
|
258 | 0 | ssize_t |
259 | 0 | i; |
260 | | |
261 | | /* |
262 | | Interpret channel expression. |
263 | | */ |
264 | 0 | switch (*token) |
265 | 0 | { |
266 | 0 | case ',': |
267 | 0 | { |
268 | 0 | (void) GetNextToken(p,&p,MagickPathExtent,token); |
269 | 0 | break; |
270 | 0 | } |
271 | 0 | case '|': |
272 | 0 | { |
273 | 0 | if (GetNextImageInList(source_image) != (Image *) NULL) |
274 | 0 | source_image=GetNextImageInList(source_image); |
275 | 0 | (void) GetNextToken(p,&p,MagickPathExtent,token); |
276 | 0 | break; |
277 | 0 | } |
278 | 0 | case ';': |
279 | 0 | { |
280 | 0 | Image |
281 | 0 | *canvas; |
282 | |
|
283 | 0 | (void) SetPixelChannelMask(destination_image,channel_mask); |
284 | 0 | if ((channel_op == ExtractChannelOp) && (channels == 1)) |
285 | 0 | { |
286 | 0 | (void) SetPixelMetaChannels(destination_image,0,exception); |
287 | 0 | (void) SetImageColorspace(destination_image,GRAYColorspace, |
288 | 0 | exception); |
289 | 0 | } |
290 | 0 | canvas=CloneImage(source_image,0,0,MagickTrue,exception); |
291 | 0 | if (canvas == (Image *) NULL) |
292 | 0 | { |
293 | 0 | destination_image=DestroyImageList(destination_image); |
294 | 0 | return(destination_image); |
295 | 0 | } |
296 | 0 | AppendImageToList(&destination_image,canvas); |
297 | 0 | destination_image=GetLastImageInList(destination_image); |
298 | 0 | status=SetImageStorageClass(destination_image,DirectClass,exception); |
299 | 0 | if (status == MagickFalse) |
300 | 0 | { |
301 | 0 | destination_image=GetLastImageInList(destination_image); |
302 | 0 | return((Image *) NULL); |
303 | 0 | } |
304 | 0 | (void) GetNextToken(p,&p,MagickPathExtent,token); |
305 | 0 | channels=0; |
306 | 0 | destination_channel=RedPixelChannel; |
307 | 0 | channel_mask=destination_image->channel_mask; |
308 | 0 | break; |
309 | 0 | } |
310 | 0 | default: |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | i=ParsePixelChannelOption(token); |
314 | 0 | source_channel=(PixelChannel) i; |
315 | 0 | traits=GetPixelChannelTraits(source_image,source_channel); |
316 | 0 | if (traits == UndefinedPixelTrait) |
317 | 0 | { |
318 | 0 | (void) ThrowMagickException(exception,GetMagickModule(), |
319 | 0 | CorruptImageError,"MissingImageChannel","`%s'",token); |
320 | 0 | destination_image=DestroyImageList(destination_image); |
321 | 0 | return(destination_image); |
322 | 0 | } |
323 | 0 | channel_op=ExtractChannelOp; |
324 | 0 | (void) GetNextToken(p,&p,MagickPathExtent,token); |
325 | 0 | if (*token == '<') |
326 | 0 | { |
327 | 0 | channel_op=ExchangeChannelOp; |
328 | 0 | (void) GetNextToken(p,&p,MagickPathExtent,token); |
329 | 0 | } |
330 | 0 | if (*token == '=') |
331 | 0 | { |
332 | 0 | if (channel_op != ExchangeChannelOp) |
333 | 0 | channel_op=AssignChannelOp; |
334 | 0 | (void) GetNextToken(p,&p,MagickPathExtent,token); |
335 | 0 | } |
336 | 0 | if (*token == '>') |
337 | 0 | { |
338 | 0 | if (channel_op != ExchangeChannelOp) |
339 | 0 | channel_op=TransferChannelOp; |
340 | 0 | (void) GetNextToken(p,&p,MagickPathExtent,token); |
341 | 0 | } |
342 | 0 | switch (channel_op) |
343 | 0 | { |
344 | 0 | case AssignChannelOp: |
345 | 0 | case ExchangeChannelOp: |
346 | 0 | case TransferChannelOp: |
347 | 0 | { |
348 | 0 | if (channel_op == AssignChannelOp) |
349 | 0 | pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0); |
350 | 0 | else |
351 | 0 | { |
352 | 0 | i=ParsePixelChannelOption(token); |
353 | 0 | if (LocaleCompare(token,"alpha") == 0) |
354 | 0 | destination_image->alpha_trait=BlendPixelTrait; |
355 | 0 | if (i < 0) |
356 | 0 | { |
357 | 0 | (void) ThrowMagickException(exception,GetMagickModule(), |
358 | 0 | OptionError,"UnrecognizedChannelType","`%s'",token); |
359 | 0 | destination_image=DestroyImageList(destination_image); |
360 | 0 | return(destination_image); |
361 | 0 | } |
362 | 0 | } |
363 | 0 | destination_channel=(PixelChannel) i; |
364 | 0 | if (image->colorspace != UndefinedColorspace) |
365 | 0 | switch (destination_channel) |
366 | 0 | { |
367 | 0 | case RedPixelChannel: |
368 | 0 | case GreenPixelChannel: |
369 | 0 | case BluePixelChannel: |
370 | 0 | case BlackPixelChannel: |
371 | 0 | case AlphaPixelChannel: |
372 | 0 | case IndexPixelChannel: |
373 | 0 | break; |
374 | 0 | case CompositeMaskPixelChannel: |
375 | 0 | { |
376 | 0 | destination_image->channels=(ChannelType) |
377 | 0 | (destination_image->channels | CompositeMaskChannel); |
378 | 0 | break; |
379 | 0 | } |
380 | 0 | case ReadMaskPixelChannel: |
381 | 0 | { |
382 | 0 | destination_image->channels=(ChannelType) |
383 | 0 | (destination_image->channels | ReadMaskChannel); |
384 | 0 | break; |
385 | 0 | } |
386 | 0 | case WriteMaskPixelChannel: |
387 | 0 | { |
388 | 0 | destination_image->channels=(ChannelType) |
389 | 0 | (destination_image->channels | WriteMaskChannel); |
390 | 0 | break; |
391 | 0 | } |
392 | 0 | case MetaPixelChannels: |
393 | 0 | default: |
394 | 0 | { |
395 | 0 | traits=GetPixelChannelTraits(destination_image, |
396 | 0 | destination_channel); |
397 | 0 | if (traits != UndefinedPixelTrait) |
398 | 0 | break; |
399 | 0 | (void) SetPixelMetaChannels(destination_image, |
400 | 0 | GetPixelMetaChannels(destination_image)+1,exception); |
401 | 0 | traits=GetPixelChannelTraits(destination_image, |
402 | 0 | destination_channel); |
403 | 0 | if (traits == UndefinedPixelTrait) |
404 | 0 | { |
405 | 0 | (void) ThrowMagickException(exception,GetMagickModule(), |
406 | 0 | CorruptImageError,"MissingImageChannel","`%s'",token); |
407 | 0 | destination_image=DestroyImageList(destination_image); |
408 | 0 | return(destination_image); |
409 | 0 | } |
410 | 0 | break; |
411 | 0 | } |
412 | 0 | } |
413 | 0 | channel_mask=(ChannelType) (channel_mask | |
414 | 0 | (MagickLLConstant(1) << ParseChannelOption(token))); |
415 | 0 | (void) GetNextToken(p,&p,MagickPathExtent,token); |
416 | 0 | break; |
417 | 0 | } |
418 | 0 | default: |
419 | 0 | break; |
420 | 0 | } |
421 | 0 | status=ChannelImage(destination_image,destination_channel,channel_op, |
422 | 0 | source_image,source_channel,ClampToQuantum(pixel),exception); |
423 | 0 | if (status == MagickFalse) |
424 | 0 | { |
425 | 0 | destination_image=DestroyImageList(destination_image); |
426 | 0 | break; |
427 | 0 | } |
428 | 0 | channels++; |
429 | 0 | if (channel_op == ExchangeChannelOp) |
430 | 0 | { |
431 | 0 | status=ChannelImage(destination_image,source_channel,channel_op, |
432 | 0 | source_image,destination_channel,ClampToQuantum(pixel),exception); |
433 | 0 | if (status == MagickFalse) |
434 | 0 | { |
435 | 0 | destination_image=DestroyImageList(destination_image); |
436 | 0 | break; |
437 | 0 | } |
438 | 0 | channels++; |
439 | 0 | } |
440 | 0 | switch (channel_op) |
441 | 0 | { |
442 | 0 | case ExtractChannelOp: |
443 | 0 | { |
444 | 0 | channel_mask=(ChannelType) (channel_mask | |
445 | 0 | (MagickLLConstant(1) << destination_channel)); |
446 | 0 | destination_channel=(PixelChannel) (destination_channel+1); |
447 | 0 | break; |
448 | 0 | } |
449 | 0 | default: |
450 | 0 | break; |
451 | 0 | } |
452 | 0 | status=SetImageProgress(source_image,ChannelFxImageTag,p-expression, |
453 | 0 | strlen(expression)); |
454 | 0 | if (status == MagickFalse) |
455 | 0 | break; |
456 | 0 | } |
457 | 0 | if (destination_image == (Image *) NULL) |
458 | 0 | return(destination_image); |
459 | 0 | (void) SetPixelChannelMask(destination_image,channel_mask); |
460 | 0 | if ((channel_op == ExtractChannelOp) && (channels == 1)) |
461 | 0 | { |
462 | 0 | (void) SetPixelMetaChannels(destination_image,0,exception); |
463 | 0 | (void) SetImageColorspace(destination_image,GRAYColorspace,exception); |
464 | 0 | } |
465 | 0 | return(GetFirstImageInList(destination_image)); |
466 | 0 | } |
467 | | |
468 | | /* |
469 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
470 | | % % |
471 | | % % |
472 | | % % |
473 | | % C o m b i n e I m a g e s % |
474 | | % % |
475 | | % % |
476 | | % % |
477 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
478 | | % |
479 | | % CombineImages() combines one or more images into a single image. The |
480 | | % grayscale value of the pixels of each image in the sequence is assigned in |
481 | | % order to the specified channels of the combined image. The typical |
482 | | % ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc. |
483 | | % |
484 | | % The format of the CombineImages method is: |
485 | | % |
486 | | % Image *CombineImages(const Image *images,const ColorspaceType colorspace, |
487 | | % ExceptionInfo *exception) |
488 | | % |
489 | | % A description of each parameter follows: |
490 | | % |
491 | | % o images: the image sequence. |
492 | | % |
493 | | % o colorspace: the image colorspace. |
494 | | % |
495 | | % o exception: return any errors or warnings in this structure. |
496 | | % |
497 | | */ |
498 | | MagickExport Image *CombineImages(const Image *image, |
499 | | const ColorspaceType colorspace,ExceptionInfo *exception) |
500 | 0 | { |
501 | 0 | #define CombineImageTag "Combine/Image" |
502 | |
|
503 | 0 | CacheView |
504 | 0 | *combine_view; |
505 | |
|
506 | 0 | Image |
507 | 0 | *combine_image; |
508 | |
|
509 | 0 | MagickBooleanType |
510 | 0 | status; |
511 | |
|
512 | 0 | MagickOffsetType |
513 | 0 | progress; |
514 | |
|
515 | 0 | size_t |
516 | 0 | number_channels; |
517 | |
|
518 | 0 | ssize_t |
519 | 0 | y; |
520 | | |
521 | | /* |
522 | | Ensure the image are the same size. |
523 | | */ |
524 | 0 | assert(image != (const Image *) NULL); |
525 | 0 | assert(image->signature == MagickCoreSignature); |
526 | 0 | if (IsEventLogging() != MagickFalse) |
527 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
528 | 0 | assert(exception != (ExceptionInfo *) NULL); |
529 | 0 | assert(exception->signature == MagickCoreSignature); |
530 | 0 | combine_image=CloneImage(image,0,0,MagickTrue,exception); |
531 | 0 | if (combine_image == (Image *) NULL) |
532 | 0 | return((Image *) NULL); |
533 | 0 | if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse) |
534 | 0 | { |
535 | 0 | combine_image=DestroyImage(combine_image); |
536 | 0 | return((Image *) NULL); |
537 | 0 | } |
538 | 0 | if (colorspace != UndefinedColorspace) |
539 | 0 | (void) SetImageColorspace(combine_image,colorspace,exception); |
540 | 0 | else |
541 | 0 | if (fabs(image->gamma-1.0) <= MagickEpsilon) |
542 | 0 | (void) SetImageColorspace(combine_image,RGBColorspace,exception); |
543 | 0 | else |
544 | 0 | (void) SetImageColorspace(combine_image,sRGBColorspace,exception); |
545 | 0 | number_channels=GetImageListLength(image); |
546 | 0 | switch (combine_image->colorspace) |
547 | 0 | { |
548 | 0 | case UndefinedColorspace: |
549 | 0 | case sRGBColorspace: |
550 | 0 | { |
551 | 0 | if (number_channels > 3) |
552 | 0 | combine_image->alpha_trait=BlendPixelTrait; |
553 | 0 | if (number_channels > 4) |
554 | 0 | SetPixelMetaChannels(combine_image,number_channels-4,exception); |
555 | 0 | break; |
556 | 0 | } |
557 | 0 | case LinearGRAYColorspace: |
558 | 0 | case GRAYColorspace: |
559 | 0 | { |
560 | 0 | if (number_channels > 1) |
561 | 0 | combine_image->alpha_trait=BlendPixelTrait; |
562 | 0 | if (number_channels > 2) |
563 | 0 | SetPixelMetaChannels(combine_image,number_channels-2,exception); |
564 | 0 | break; |
565 | 0 | } |
566 | 0 | case CMYKColorspace: |
567 | 0 | { |
568 | 0 | if (number_channels > 4) |
569 | 0 | combine_image->alpha_trait=BlendPixelTrait; |
570 | 0 | if (number_channels > 5) |
571 | 0 | SetPixelMetaChannels(combine_image,number_channels-5,exception); |
572 | 0 | break; |
573 | 0 | } |
574 | 0 | default: |
575 | 0 | break; |
576 | 0 | } |
577 | | /* |
578 | | Combine images. |
579 | | */ |
580 | 0 | status=MagickTrue; |
581 | 0 | progress=0; |
582 | 0 | combine_view=AcquireAuthenticCacheView(combine_image,exception); |
583 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
584 | | #pragma omp parallel for schedule(static) shared(progress,status) \ |
585 | | magick_number_threads(combine_image,combine_image,combine_image->rows,4) |
586 | | #endif |
587 | 0 | for (y=0; y < (ssize_t) combine_image->rows; y++) |
588 | 0 | { |
589 | 0 | CacheView |
590 | 0 | *image_view; |
591 | |
|
592 | 0 | const Image |
593 | 0 | *next; |
594 | |
|
595 | 0 | const Quantum |
596 | 0 | *magick_restrict p; |
597 | |
|
598 | 0 | Quantum |
599 | 0 | *pixels, |
600 | 0 | *magick_restrict q; |
601 | |
|
602 | 0 | ssize_t |
603 | 0 | i; |
604 | |
|
605 | 0 | if (status == MagickFalse) |
606 | 0 | continue; |
607 | 0 | pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns, |
608 | 0 | 1,exception); |
609 | 0 | if (pixels == (Quantum *) NULL) |
610 | 0 | { |
611 | 0 | status=MagickFalse; |
612 | 0 | continue; |
613 | 0 | } |
614 | 0 | next=image; |
615 | 0 | for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++) |
616 | 0 | { |
617 | 0 | ssize_t |
618 | 0 | x; |
619 | |
|
620 | 0 | PixelChannel channel = GetPixelChannelChannel(combine_image,i); |
621 | 0 | PixelTrait traits = GetPixelChannelTraits(combine_image,channel); |
622 | 0 | if (traits == UndefinedPixelTrait) |
623 | 0 | continue; |
624 | 0 | if (next == (Image *) NULL) |
625 | 0 | continue; |
626 | 0 | image_view=AcquireVirtualCacheView(next,exception); |
627 | 0 | p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception); |
628 | 0 | if (p == (const Quantum *) NULL) |
629 | 0 | continue; |
630 | 0 | q=pixels; |
631 | 0 | for (x=0; x < (ssize_t) combine_image->columns; x++) |
632 | 0 | { |
633 | 0 | if (x < (ssize_t) next->columns) |
634 | 0 | { |
635 | 0 | q[i]=(Quantum) GetPixelIntensity(next,p); |
636 | 0 | p+=(ptrdiff_t) GetPixelChannels(next); |
637 | 0 | } |
638 | 0 | q+=(ptrdiff_t) GetPixelChannels(combine_image); |
639 | 0 | } |
640 | 0 | image_view=DestroyCacheView(image_view); |
641 | 0 | next=GetNextImageInList(next); |
642 | 0 | } |
643 | 0 | if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse) |
644 | 0 | status=MagickFalse; |
645 | 0 | if (image->progress_monitor != (MagickProgressMonitor) NULL) |
646 | 0 | { |
647 | 0 | MagickBooleanType |
648 | 0 | proceed; |
649 | |
|
650 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
651 | | #pragma omp atomic |
652 | | #endif |
653 | 0 | progress++; |
654 | 0 | proceed=SetImageProgress(image,CombineImageTag,progress, |
655 | 0 | combine_image->rows); |
656 | 0 | if (proceed == MagickFalse) |
657 | 0 | status=MagickFalse; |
658 | 0 | } |
659 | 0 | } |
660 | 0 | combine_view=DestroyCacheView(combine_view); |
661 | 0 | if (status == MagickFalse) |
662 | 0 | combine_image=DestroyImage(combine_image); |
663 | 0 | else |
664 | 0 | combine_image->type=UndefinedType; |
665 | 0 | return(combine_image); |
666 | 0 | } |
667 | | |
668 | | /* |
669 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
670 | | % % |
671 | | % % |
672 | | % % |
673 | | % G e t I m a g e A l p h a C h a n n e l % |
674 | | % % |
675 | | % % |
676 | | % % |
677 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
678 | | % |
679 | | % GetImageAlphaChannel() returns MagickFalse if the image alpha channel is |
680 | | % not activated. That is, the image is RGB rather than RGBA or CMYK rather |
681 | | % than CMYKA. |
682 | | % |
683 | | % The format of the GetImageAlphaChannel method is: |
684 | | % |
685 | | % MagickBooleanType GetImageAlphaChannel(const Image *image) |
686 | | % |
687 | | % A description of each parameter follows: |
688 | | % |
689 | | % o image: the image. |
690 | | % |
691 | | */ |
692 | | MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image) |
693 | 0 | { |
694 | 0 | assert(image != (const Image *) NULL); |
695 | 0 | if (IsEventLogging() != MagickFalse) |
696 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); |
697 | 0 | assert(image->signature == MagickCoreSignature); |
698 | 0 | return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse); |
699 | 0 | } |
700 | | |
701 | | /* |
702 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
703 | | % % |
704 | | % % |
705 | | % % |
706 | | % S e p a r a t e I m a g e % |
707 | | % % |
708 | | % % |
709 | | % % |
710 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
711 | | % |
712 | | % SeparateImage() separates a channel from the image and returns it as a |
713 | | % grayscale image. |
714 | | % |
715 | | % The format of the SeparateImage method is: |
716 | | % |
717 | | % Image *SeparateImage(const Image *image,const ChannelType channel, |
718 | | % ExceptionInfo *exception) |
719 | | % |
720 | | % A description of each parameter follows: |
721 | | % |
722 | | % o image: the image. |
723 | | % |
724 | | % o channel: the image channel. |
725 | | % |
726 | | % o exception: return any errors or warnings in this structure. |
727 | | % |
728 | | */ |
729 | | MagickExport Image *SeparateImage(const Image *image, |
730 | | const ChannelType channel_type,ExceptionInfo *exception) |
731 | 351k | { |
732 | 1.72G | #define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01) |
733 | 351k | #define SeparateImageTag "Separate/Image" |
734 | | |
735 | 351k | CacheView |
736 | 351k | *image_view, |
737 | 351k | *separate_view; |
738 | | |
739 | 351k | Image |
740 | 351k | *separate_image; |
741 | | |
742 | 351k | MagickBooleanType |
743 | 351k | status; |
744 | | |
745 | 351k | MagickOffsetType |
746 | 351k | progress; |
747 | | |
748 | 351k | ssize_t |
749 | 351k | y; |
750 | | |
751 | | /* |
752 | | Initialize separate image attributes. |
753 | | */ |
754 | 351k | assert(image != (Image *) NULL); |
755 | 351k | assert(image->signature == MagickCoreSignature); |
756 | 351k | if (IsEventLogging() != MagickFalse) |
757 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
758 | 351k | assert(exception != (ExceptionInfo *) NULL); |
759 | 351k | assert(exception->signature == MagickCoreSignature); |
760 | 351k | separate_image=CloneImage(image,0,0,MagickTrue,exception); |
761 | 351k | if (separate_image == (Image *) NULL) |
762 | 0 | return((Image *) NULL); |
763 | 351k | if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse) |
764 | 0 | { |
765 | 0 | separate_image=DestroyImage(separate_image); |
766 | 0 | return((Image *) NULL); |
767 | 0 | } |
768 | 351k | separate_image->alpha_trait=UndefinedPixelTrait; |
769 | 351k | (void) SetImageColorspace(separate_image,GRAYColorspace,exception); |
770 | 351k | separate_image->gamma=image->gamma; |
771 | | /* |
772 | | Separate image. |
773 | | */ |
774 | 351k | status=MagickTrue; |
775 | 351k | progress=0; |
776 | 351k | image_view=AcquireVirtualCacheView(image,exception); |
777 | 351k | separate_view=AcquireAuthenticCacheView(separate_image,exception); |
778 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
779 | | #pragma omp parallel for schedule(static) shared(progress,status) \ |
780 | | magick_number_threads(image,image,image->rows,2) |
781 | | #endif |
782 | 9.23M | for (y=0; y < (ssize_t) image->rows; y++) |
783 | 8.88M | { |
784 | 8.88M | const Quantum |
785 | 8.88M | *magick_restrict p; |
786 | | |
787 | 8.88M | Quantum |
788 | 8.88M | *magick_restrict q; |
789 | | |
790 | 8.88M | ssize_t |
791 | 8.88M | x; |
792 | | |
793 | 8.88M | if (status == MagickFalse) |
794 | 0 | continue; |
795 | 8.88M | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
796 | 8.88M | q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1, |
797 | 8.88M | exception); |
798 | 8.88M | if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) |
799 | 0 | { |
800 | 0 | status=MagickFalse; |
801 | 0 | continue; |
802 | 0 | } |
803 | 393M | for (x=0; x < (ssize_t) image->columns; x++) |
804 | 384M | { |
805 | 384M | ssize_t |
806 | 384M | i; |
807 | | |
808 | 384M | SetPixelChannel(separate_image,GrayPixelChannel,(Quantum) 0,q); |
809 | 2.11G | for (i=0; i < (ssize_t) GetPixelChannels(image); i++) |
810 | 1.72G | { |
811 | 1.72G | PixelChannel channel = GetPixelChannelChannel(image,i); |
812 | 1.72G | PixelTrait traits = GetPixelChannelTraits(image,channel); |
813 | 1.72G | if ((traits == UndefinedPixelTrait) || |
814 | 1.72G | (GetChannelBit(channel_type,channel) == 0)) |
815 | 1.34G | continue; |
816 | 384M | SetPixelChannel(separate_image,GrayPixelChannel,p[i],q); |
817 | 384M | } |
818 | 384M | p+=(ptrdiff_t) GetPixelChannels(image); |
819 | 384M | q+=(ptrdiff_t) GetPixelChannels(separate_image); |
820 | 384M | } |
821 | 8.88M | if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse) |
822 | 0 | status=MagickFalse; |
823 | 8.88M | if (image->progress_monitor != (MagickProgressMonitor) NULL) |
824 | 0 | { |
825 | 0 | MagickBooleanType |
826 | 0 | proceed; |
827 | |
|
828 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
829 | | #pragma omp atomic |
830 | | #endif |
831 | 0 | progress++; |
832 | 0 | proceed=SetImageProgress(image,SeparateImageTag,progress,image->rows); |
833 | 0 | if (proceed == MagickFalse) |
834 | 0 | status=MagickFalse; |
835 | 0 | } |
836 | 8.88M | } |
837 | 351k | separate_view=DestroyCacheView(separate_view); |
838 | 351k | image_view=DestroyCacheView(image_view); |
839 | 351k | (void) SetImageChannelMask(separate_image,AllChannels); |
840 | 351k | if (status == MagickFalse) |
841 | 0 | separate_image=DestroyImage(separate_image); |
842 | 351k | return(separate_image); |
843 | 351k | } |
844 | | |
845 | | /* |
846 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
847 | | % % |
848 | | % % |
849 | | % % |
850 | | % S e p a r a t e I m a g e s % |
851 | | % % |
852 | | % % |
853 | | % % |
854 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
855 | | % |
856 | | % SeparateImages() returns a separate grayscale image for each channel |
857 | | % specified. |
858 | | % |
859 | | % The format of the SeparateImages method is: |
860 | | % |
861 | | % Image *SeparateImages(const Image *image,ExceptionInfo *exception) |
862 | | % |
863 | | % A description of each parameter follows: |
864 | | % |
865 | | % o image: the image. |
866 | | % |
867 | | % o exception: return any errors or warnings in this structure. |
868 | | % |
869 | | */ |
870 | | MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception) |
871 | 0 | { |
872 | 0 | Image |
873 | 0 | *images, |
874 | 0 | *separate_image; |
875 | |
|
876 | 0 | ssize_t |
877 | 0 | i; |
878 | |
|
879 | 0 | assert(image != (Image *) NULL); |
880 | 0 | assert(image->signature == MagickCoreSignature); |
881 | 0 | if (IsEventLogging() != MagickFalse) |
882 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
883 | 0 | images=NewImageList(); |
884 | 0 | for (i=0; i < (ssize_t) GetPixelChannels(image); i++) |
885 | 0 | { |
886 | 0 | PixelChannel channel = GetPixelChannelChannel(image,i); |
887 | 0 | PixelTrait traits = GetPixelChannelTraits(image,channel); |
888 | 0 | if ((traits == UndefinedPixelTrait) || ((traits & UpdatePixelTrait) == 0)) |
889 | 0 | continue; |
890 | 0 | separate_image=SeparateImage(image,(ChannelType) |
891 | 0 | (MagickLLConstant(1) << channel),exception); |
892 | 0 | if (separate_image != (Image *) NULL) |
893 | 0 | AppendImageToList(&images,separate_image); |
894 | 0 | } |
895 | 0 | if (images == (Image *) NULL) |
896 | 0 | images=SeparateImage(image,UndefinedChannel,exception); |
897 | 0 | return(images); |
898 | 0 | } |
899 | | |
900 | | /* |
901 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
902 | | % % |
903 | | % % |
904 | | % % |
905 | | % S e t I m a g e A l p h a C h a n n e l % |
906 | | % % |
907 | | % % |
908 | | % % |
909 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
910 | | % |
911 | | % SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha |
912 | | % channel. |
913 | | % |
914 | | % The format of the SetImageAlphaChannel method is: |
915 | | % |
916 | | % MagickBooleanType SetImageAlphaChannel(Image *image, |
917 | | % const AlphaChannelOption alpha_type,ExceptionInfo *exception) |
918 | | % |
919 | | % A description of each parameter follows: |
920 | | % |
921 | | % o image: the image. |
922 | | % |
923 | | % o alpha_type: The alpha channel type: ActivateAlphaChannel, |
924 | | % AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel, |
925 | | % DisassociateAlphaChannel, ExtractAlphaChannel, OffAlphaChannel, |
926 | | % OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel, |
927 | | % and TransparentAlphaChannel. |
928 | | % |
929 | | % o exception: return any errors or warnings in this structure. |
930 | | % |
931 | | */ |
932 | | |
933 | | static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p, |
934 | | const double alpha,const Quantum *q,const double beta,Quantum *composite) |
935 | 0 | { |
936 | 0 | double |
937 | 0 | Da, |
938 | 0 | gamma, |
939 | 0 | Sa; |
940 | |
|
941 | 0 | ssize_t |
942 | 0 | i; |
943 | | |
944 | | /* |
945 | | Compose pixel p over pixel q with the given alpha. |
946 | | */ |
947 | 0 | Sa=QuantumScale*alpha; |
948 | 0 | Da=QuantumScale*beta, |
949 | 0 | gamma=Sa*(-Da)+Sa+Da; |
950 | 0 | gamma=MagickSafeReciprocal(gamma); |
951 | 0 | for (i=0; i < (ssize_t) GetPixelChannels(image); i++) |
952 | 0 | { |
953 | 0 | PixelChannel channel = GetPixelChannelChannel(image,i); |
954 | 0 | PixelTrait traits = GetPixelChannelTraits(image,channel); |
955 | 0 | if (traits == UndefinedPixelTrait) |
956 | 0 | continue; |
957 | 0 | switch (channel) |
958 | 0 | { |
959 | 0 | case RedPixelChannel: |
960 | 0 | { |
961 | 0 | composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta, |
962 | 0 | (double) p->red,alpha)); |
963 | 0 | break; |
964 | 0 | } |
965 | 0 | case GreenPixelChannel: |
966 | 0 | { |
967 | 0 | composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta, |
968 | 0 | (double) p->green,alpha)); |
969 | 0 | break; |
970 | 0 | } |
971 | 0 | case BluePixelChannel: |
972 | 0 | { |
973 | 0 | composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta, |
974 | 0 | (double) p->blue,alpha)); |
975 | 0 | break; |
976 | 0 | } |
977 | 0 | case BlackPixelChannel: |
978 | 0 | { |
979 | 0 | composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta, |
980 | 0 | (double) p->black,alpha)); |
981 | 0 | break; |
982 | 0 | } |
983 | 0 | case AlphaPixelChannel: |
984 | 0 | { |
985 | 0 | composite[i]=ClampToQuantum((double) QuantumRange*(Sa*(-Da)+Sa+Da)); |
986 | 0 | break; |
987 | 0 | } |
988 | 0 | default: |
989 | 0 | break; |
990 | 0 | } |
991 | 0 | } |
992 | 0 | } |
993 | | |
994 | | MagickExport MagickBooleanType SetImageAlphaChannel(Image *image, |
995 | | const AlphaChannelOption alpha_type,ExceptionInfo *exception) |
996 | 383k | { |
997 | 383k | CacheView |
998 | 383k | *image_view; |
999 | | |
1000 | 383k | MagickBooleanType |
1001 | 383k | status; |
1002 | | |
1003 | 383k | ssize_t |
1004 | 383k | y; |
1005 | | |
1006 | 383k | assert(image != (Image *) NULL); |
1007 | 383k | if (IsEventLogging() != MagickFalse) |
1008 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); |
1009 | 383k | assert(image->signature == MagickCoreSignature); |
1010 | 383k | status=MagickTrue; |
1011 | 383k | switch (alpha_type) |
1012 | 383k | { |
1013 | 362k | case ActivateAlphaChannel: |
1014 | 362k | { |
1015 | 362k | if ((image->alpha_trait & BlendPixelTrait) != 0) |
1016 | 0 | return(status); |
1017 | 362k | image->alpha_trait=BlendPixelTrait; |
1018 | 362k | break; |
1019 | 362k | } |
1020 | 0 | case AssociateAlphaChannel: |
1021 | 0 | { |
1022 | | /* |
1023 | | Associate alpha. |
1024 | | */ |
1025 | 0 | status=SetImageStorageClass(image,DirectClass,exception); |
1026 | 0 | if (status == MagickFalse) |
1027 | 0 | break; |
1028 | 0 | image_view=AcquireAuthenticCacheView(image,exception); |
1029 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1030 | | #pragma omp parallel for schedule(static) shared(status) \ |
1031 | | magick_number_threads(image,image,image->rows,2) |
1032 | | #endif |
1033 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
1034 | 0 | { |
1035 | 0 | Quantum |
1036 | 0 | *magick_restrict q; |
1037 | |
|
1038 | 0 | ssize_t |
1039 | 0 | x; |
1040 | |
|
1041 | 0 | if (status == MagickFalse) |
1042 | 0 | continue; |
1043 | 0 | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, |
1044 | 0 | exception); |
1045 | 0 | if (q == (Quantum *) NULL) |
1046 | 0 | { |
1047 | 0 | status=MagickFalse; |
1048 | 0 | continue; |
1049 | 0 | } |
1050 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
1051 | 0 | { |
1052 | 0 | double |
1053 | 0 | gamma; |
1054 | |
|
1055 | 0 | ssize_t |
1056 | 0 | i; |
1057 | |
|
1058 | 0 | gamma=QuantumScale*(double) GetPixelAlpha(image,q); |
1059 | 0 | for (i=0; i < (ssize_t) GetPixelChannels(image); i++) |
1060 | 0 | { |
1061 | 0 | PixelChannel channel = GetPixelChannelChannel(image,i); |
1062 | 0 | PixelTrait traits = GetPixelChannelTraits(image,channel); |
1063 | 0 | if (channel == AlphaPixelChannel) |
1064 | 0 | continue; |
1065 | 0 | if ((traits & UpdatePixelTrait) == 0) |
1066 | 0 | continue; |
1067 | 0 | q[i]=ClampToQuantum(gamma*(double) q[i]); |
1068 | 0 | } |
1069 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
1070 | 0 | } |
1071 | 0 | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
1072 | 0 | status=MagickFalse; |
1073 | 0 | } |
1074 | 0 | image_view=DestroyCacheView(image_view); |
1075 | 0 | image->alpha_trait=CopyPixelTrait; |
1076 | 0 | return(status); |
1077 | 0 | } |
1078 | 0 | case BackgroundAlphaChannel: |
1079 | 0 | { |
1080 | | /* |
1081 | | Set transparent pixels to background color. |
1082 | | */ |
1083 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1084 | 0 | break; |
1085 | 0 | status=SetImageStorageClass(image,DirectClass,exception); |
1086 | 0 | if (status == MagickFalse) |
1087 | 0 | break; |
1088 | 0 | image_view=AcquireAuthenticCacheView(image,exception); |
1089 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1090 | | #pragma omp parallel for schedule(static) shared(status) \ |
1091 | | magick_number_threads(image,image,image->rows,2) |
1092 | | #endif |
1093 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
1094 | 0 | { |
1095 | 0 | Quantum |
1096 | 0 | *magick_restrict q; |
1097 | |
|
1098 | 0 | ssize_t |
1099 | 0 | x; |
1100 | |
|
1101 | 0 | if (status == MagickFalse) |
1102 | 0 | continue; |
1103 | 0 | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, |
1104 | 0 | exception); |
1105 | 0 | if (q == (Quantum *) NULL) |
1106 | 0 | { |
1107 | 0 | status=MagickFalse; |
1108 | 0 | continue; |
1109 | 0 | } |
1110 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
1111 | 0 | { |
1112 | 0 | if (GetPixelAlpha(image,q) == TransparentAlpha) |
1113 | 0 | { |
1114 | 0 | SetPixelViaPixelInfo(image,&image->background_color,q); |
1115 | 0 | SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q); |
1116 | 0 | } |
1117 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
1118 | 0 | } |
1119 | 0 | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
1120 | 0 | status=MagickFalse; |
1121 | 0 | } |
1122 | 0 | image_view=DestroyCacheView(image_view); |
1123 | 0 | return(status); |
1124 | 0 | } |
1125 | 0 | case CopyAlphaChannel: |
1126 | 0 | { |
1127 | 0 | image->alpha_trait=UpdatePixelTrait; |
1128 | 0 | status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0, |
1129 | 0 | exception); |
1130 | 0 | break; |
1131 | 0 | } |
1132 | 0 | case DeactivateAlphaChannel: |
1133 | 0 | { |
1134 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1135 | 0 | status=SetImageAlpha(image,OpaqueAlpha,exception); |
1136 | 0 | image->alpha_trait=CopyPixelTrait; |
1137 | 0 | break; |
1138 | 0 | } |
1139 | 0 | case DisassociateAlphaChannel: |
1140 | 0 | { |
1141 | | /* |
1142 | | Disassociate alpha. |
1143 | | */ |
1144 | 0 | status=SetImageStorageClass(image,DirectClass,exception); |
1145 | 0 | if (status == MagickFalse) |
1146 | 0 | break; |
1147 | 0 | image->alpha_trait=BlendPixelTrait; |
1148 | 0 | image_view=AcquireAuthenticCacheView(image,exception); |
1149 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1150 | | #pragma omp parallel for schedule(static) shared(status) \ |
1151 | | magick_number_threads(image,image,image->rows,2) |
1152 | | #endif |
1153 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
1154 | 0 | { |
1155 | 0 | Quantum |
1156 | 0 | *magick_restrict q; |
1157 | |
|
1158 | 0 | ssize_t |
1159 | 0 | x; |
1160 | |
|
1161 | 0 | if (status == MagickFalse) |
1162 | 0 | continue; |
1163 | 0 | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, |
1164 | 0 | exception); |
1165 | 0 | if (q == (Quantum *) NULL) |
1166 | 0 | { |
1167 | 0 | status=MagickFalse; |
1168 | 0 | continue; |
1169 | 0 | } |
1170 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
1171 | 0 | { |
1172 | 0 | double |
1173 | 0 | gamma, |
1174 | 0 | Sa; |
1175 | |
|
1176 | 0 | ssize_t |
1177 | 0 | i; |
1178 | |
|
1179 | 0 | Sa=QuantumScale*(double) GetPixelAlpha(image,q); |
1180 | 0 | gamma=MagickSafeReciprocal(Sa); |
1181 | 0 | for (i=0; i < (ssize_t) GetPixelChannels(image); i++) |
1182 | 0 | { |
1183 | 0 | PixelChannel channel = GetPixelChannelChannel(image,i); |
1184 | 0 | PixelTrait traits = GetPixelChannelTraits(image,channel); |
1185 | 0 | if (channel == AlphaPixelChannel) |
1186 | 0 | continue; |
1187 | 0 | if ((traits & UpdatePixelTrait) == 0) |
1188 | 0 | continue; |
1189 | 0 | q[i]=ClampToQuantum(gamma*(double) q[i]); |
1190 | 0 | } |
1191 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
1192 | 0 | } |
1193 | 0 | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
1194 | 0 | status=MagickFalse; |
1195 | 0 | } |
1196 | 0 | image_view=DestroyCacheView(image_view); |
1197 | 0 | image->alpha_trait=UndefinedPixelTrait; |
1198 | 0 | return(status); |
1199 | 0 | } |
1200 | 0 | case DiscreteAlphaChannel: |
1201 | 0 | { |
1202 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1203 | 0 | status=SetImageAlpha(image,OpaqueAlpha,exception); |
1204 | 0 | image->alpha_trait=UpdatePixelTrait; |
1205 | 0 | break; |
1206 | 0 | } |
1207 | 0 | case ExtractAlphaChannel: |
1208 | 0 | { |
1209 | 0 | status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0, |
1210 | 0 | exception); |
1211 | 0 | image->alpha_trait=UndefinedPixelTrait; |
1212 | 0 | break; |
1213 | 0 | } |
1214 | 0 | case OffAlphaChannel: |
1215 | 0 | { |
1216 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1217 | 0 | return(status); |
1218 | 0 | image->alpha_trait=UndefinedPixelTrait; |
1219 | 0 | break; |
1220 | 0 | } |
1221 | 0 | case OffIfOpaqueAlphaChannel: |
1222 | 0 | { |
1223 | 0 | MagickBooleanType |
1224 | 0 | opaque = MagickTrue; |
1225 | | |
1226 | | /* |
1227 | | Remove opaque alpha channel. |
1228 | | */ |
1229 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1230 | 0 | break; |
1231 | 0 | image_view=AcquireVirtualCacheView(image,exception); |
1232 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1233 | | #pragma omp parallel for schedule(static) shared(opaque,status) \ |
1234 | | magick_number_threads(image,image,image->rows,2) |
1235 | | #endif |
1236 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
1237 | 0 | { |
1238 | 0 | const Quantum |
1239 | 0 | *magick_restrict p; |
1240 | |
|
1241 | 0 | ssize_t |
1242 | 0 | x; |
1243 | |
|
1244 | 0 | if ((status == MagickFalse) || (opaque == MagickFalse)) |
1245 | 0 | continue; |
1246 | 0 | p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); |
1247 | 0 | if (p == (const Quantum *) NULL) |
1248 | 0 | { |
1249 | 0 | status=MagickFalse; |
1250 | 0 | continue; |
1251 | 0 | } |
1252 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
1253 | 0 | { |
1254 | 0 | if (GetPixelAlpha(image,p) != OpaqueAlpha) |
1255 | 0 | { |
1256 | 0 | opaque=MagickFalse; |
1257 | 0 | break; |
1258 | 0 | } |
1259 | 0 | p+=(ptrdiff_t) GetPixelChannels(image); |
1260 | 0 | } |
1261 | 0 | } |
1262 | 0 | image_view=DestroyCacheView(image_view); |
1263 | 0 | if (opaque != MagickFalse) |
1264 | 0 | image->alpha_trait=UndefinedPixelTrait; |
1265 | 0 | break; |
1266 | 0 | } |
1267 | 0 | case OnAlphaChannel: |
1268 | 0 | { |
1269 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1270 | 0 | status=SetImageAlpha(image,OpaqueAlpha,exception); |
1271 | 0 | image->alpha_trait=BlendPixelTrait; |
1272 | 0 | break; |
1273 | 0 | } |
1274 | 21.9k | case OpaqueAlphaChannel: |
1275 | 21.9k | { |
1276 | 21.9k | status=SetImageAlpha(image,OpaqueAlpha,exception); |
1277 | 21.9k | break; |
1278 | 0 | } |
1279 | 0 | case RemoveAlphaChannel: |
1280 | 0 | { |
1281 | | /* |
1282 | | Remove transparency. |
1283 | | */ |
1284 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1285 | 0 | break; |
1286 | 0 | status=SetImageStorageClass(image,DirectClass,exception); |
1287 | 0 | if (status == MagickFalse) |
1288 | 0 | break; |
1289 | 0 | image_view=AcquireAuthenticCacheView(image,exception); |
1290 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1291 | | #pragma omp parallel for schedule(static) shared(status) \ |
1292 | | magick_number_threads(image,image,image->rows,2) |
1293 | | #endif |
1294 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
1295 | 0 | { |
1296 | 0 | Quantum |
1297 | 0 | *magick_restrict q; |
1298 | |
|
1299 | 0 | ssize_t |
1300 | 0 | x; |
1301 | |
|
1302 | 0 | if (status == MagickFalse) |
1303 | 0 | continue; |
1304 | 0 | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, |
1305 | 0 | exception); |
1306 | 0 | if (q == (Quantum *) NULL) |
1307 | 0 | { |
1308 | 0 | status=MagickFalse; |
1309 | 0 | continue; |
1310 | 0 | } |
1311 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
1312 | 0 | { |
1313 | 0 | FlattenPixelInfo(image,&image->background_color, |
1314 | 0 | image->background_color.alpha,q,(double) GetPixelAlpha(image,q),q); |
1315 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
1316 | 0 | } |
1317 | 0 | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
1318 | 0 | status=MagickFalse; |
1319 | 0 | } |
1320 | 0 | image_view=DestroyCacheView(image_view); |
1321 | 0 | image->alpha_trait=image->background_color.alpha_trait; |
1322 | 0 | break; |
1323 | 0 | } |
1324 | 0 | case SetAlphaChannel: |
1325 | 0 | { |
1326 | 0 | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1327 | 0 | status=SetImageAlpha(image,OpaqueAlpha,exception); |
1328 | 0 | break; |
1329 | 0 | } |
1330 | 0 | case ShapeAlphaChannel: |
1331 | 0 | { |
1332 | 0 | PixelInfo |
1333 | 0 | background; |
1334 | | |
1335 | | /* |
1336 | | Remove transparency. |
1337 | | */ |
1338 | 0 | ConformPixelInfo(image,&image->background_color,&background,exception); |
1339 | 0 | background.alpha_trait=BlendPixelTrait; |
1340 | 0 | image->alpha_trait=BlendPixelTrait; |
1341 | 0 | status=SetImageStorageClass(image,DirectClass,exception); |
1342 | 0 | if (status == MagickFalse) |
1343 | 0 | break; |
1344 | 0 | image_view=AcquireAuthenticCacheView(image,exception); |
1345 | | #if defined(MAGICKCORE_OPENMP_SUPPORT) |
1346 | | #pragma omp parallel for schedule(static) shared(status) \ |
1347 | | magick_number_threads(image,image,image->rows,2) |
1348 | | #endif |
1349 | 0 | for (y=0; y < (ssize_t) image->rows; y++) |
1350 | 0 | { |
1351 | 0 | PixelInfo |
1352 | 0 | pixel; |
1353 | |
|
1354 | 0 | Quantum |
1355 | 0 | *magick_restrict q; |
1356 | |
|
1357 | 0 | ssize_t |
1358 | 0 | x; |
1359 | |
|
1360 | 0 | if (status == MagickFalse) |
1361 | 0 | continue; |
1362 | 0 | q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, |
1363 | 0 | exception); |
1364 | 0 | if (q == (Quantum *) NULL) |
1365 | 0 | { |
1366 | 0 | status=MagickFalse; |
1367 | 0 | continue; |
1368 | 0 | } |
1369 | 0 | pixel=background; |
1370 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
1371 | 0 | { |
1372 | 0 | pixel.alpha=GetPixelIntensity(image,q); |
1373 | 0 | SetPixelViaPixelInfo(image,&pixel,q); |
1374 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
1375 | 0 | } |
1376 | 0 | if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) |
1377 | 0 | status=MagickFalse; |
1378 | 0 | } |
1379 | 0 | image_view=DestroyCacheView(image_view); |
1380 | 0 | break; |
1381 | 0 | } |
1382 | 0 | case TransparentAlphaChannel: |
1383 | 0 | { |
1384 | 0 | status=SetImageAlpha(image,TransparentAlpha,exception); |
1385 | 0 | break; |
1386 | 0 | } |
1387 | 0 | case UndefinedAlphaChannel: |
1388 | 0 | break; |
1389 | 383k | } |
1390 | 383k | if (status == MagickFalse) |
1391 | 0 | return(status); |
1392 | 383k | (void) SetPixelChannelMask(image,image->channel_mask); |
1393 | 383k | return(SyncImagePixelCache(image,exception)); |
1394 | 383k | } |