/src/graphicsmagick/magick/channel.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2004-2026 GraphicsMagick Group |
3 | | % |
4 | | % This program is covered by multiple licenses, which are described in |
5 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
6 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
7 | | % |
8 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
9 | | % % |
10 | | % % |
11 | | % % |
12 | | % CCCC H H AAA N N N N EEEEE L % |
13 | | % C H H A A NN N NN N E L % |
14 | | % C HHHHH AAAAA N N N N N N EEE L % |
15 | | % C H H A A N NN N NN E L % |
16 | | % CCCC H H A A N N N N EEEEE LLLLL % |
17 | | % % |
18 | | % % |
19 | | % Image Channel Operations % |
20 | | % % |
21 | | % % |
22 | | % Software Design % |
23 | | % Bob Friesenhahn % |
24 | | % July 2004 % |
25 | | % % |
26 | | % % |
27 | | % % |
28 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
29 | | % |
30 | | % |
31 | | % |
32 | | */ |
33 | | |
34 | | /* |
35 | | Include declarations. |
36 | | */ |
37 | | #include "magick/studio.h" |
38 | | #include "magick/channel.h" |
39 | | #include "magick/enum_strings.h" |
40 | | #include "magick/image.h" |
41 | | #include "magick/operator.h" |
42 | | #include "magick/pixel_iterator.h" |
43 | | #include "magick/utility.h" |
44 | | |
45 | | /* |
46 | | Verify that image colorspace is compatible with with requested |
47 | | channel type. Only check mismatch between RGB and CMYK since user |
48 | | might intentionally export some obscure colorspace channel. We |
49 | | don't silently convert between RGB and CMYK since there is no one |
50 | | correct transform, and the transform is lossy. |
51 | | */ |
52 | | static MagickPassFail ValidateChannelRequest(const ColorspaceType image_colorspace, |
53 | | const ChannelType channel, |
54 | | ExceptionInfo *exception) |
55 | 0 | { |
56 | 0 | MagickPassFail |
57 | 0 | status = MagickPass; |
58 | |
|
59 | 0 | switch(channel) |
60 | 0 | { |
61 | 0 | case CyanChannel: |
62 | 0 | case MagentaChannel: |
63 | 0 | case YellowChannel: |
64 | 0 | case BlackChannel: |
65 | 0 | if (image_colorspace != CMYKColorspace) |
66 | 0 | status = MagickFail; |
67 | 0 | break; |
68 | 0 | case RedChannel: |
69 | 0 | case GreenChannel: |
70 | 0 | case BlueChannel: |
71 | 0 | if (image_colorspace == CMYKColorspace) |
72 | 0 | status = MagickFail;; |
73 | 0 | break; |
74 | 0 | default: |
75 | 0 | { |
76 | 0 | } |
77 | 0 | } |
78 | | |
79 | 0 | if (MagickFail == status) |
80 | 0 | ThrowException3(exception,ImageError,UnableToHandleImageChannel,ImageColorspaceMismatch); |
81 | |
|
82 | 0 | return status; |
83 | 0 | } |
84 | | |
85 | | /* |
86 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
87 | | % % |
88 | | % % |
89 | | % % |
90 | | % C h a n n e l I m a g e % |
91 | | % % |
92 | | % % |
93 | | % % |
94 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
95 | | % |
96 | | % Transform an image so that the resulting image is a grayscale image |
97 | | % based on a specified image channel. The resulting image is returned in |
98 | | % the RGB colorspace. This function does not force or assume an input |
99 | | % image colorspace so it may be used to extract channels from images in |
100 | | % colorspaces other than RGB or CMYK. For example, if the image is currently |
101 | | % transformed to the HWB colorspace, the 'B' channel may be extracted by |
102 | | % specifying RedChannel as the ChannelType argument. |
103 | | % |
104 | | % The format of the ChannelImage method is: |
105 | | % |
106 | | % unsigned int ChannelImage(Image *image,const ChannelType channel) |
107 | | % |
108 | | % A description of each parameter follows: |
109 | | % |
110 | | % o image: The image. |
111 | | % |
112 | | % o channel: Identify which channel to extract: Red, Cyan, Green, Magenta, |
113 | | % Blue, Yellow, or Opacity. |
114 | | % |
115 | | % |
116 | | */ |
117 | | static MagickPassFail |
118 | | ChannelImagePixels(void *mutable_data, /* User provided mutable data */ |
119 | | const void *immutable_data, /* User provided immutable data */ |
120 | | Image * restrict image, /* Modify image */ |
121 | | PixelPacket * restrict pixels, /* Pixel row */ |
122 | | IndexPacket * restrict indexes,/* Pixel row indexes */ |
123 | | const long npixels, /* Number of pixels in row */ |
124 | | ExceptionInfo *exception) /* Exception report */ |
125 | 0 | { |
126 | | /* |
127 | | Transform image so that it only represents the specified channel. |
128 | | */ |
129 | 0 | ChannelType |
130 | 0 | channel = *((const ChannelType *) immutable_data); |
131 | |
|
132 | 0 | register long |
133 | 0 | i; |
134 | |
|
135 | 0 | ARG_NOT_USED(mutable_data); |
136 | 0 | ARG_NOT_USED(exception); |
137 | |
|
138 | 0 | switch (channel) |
139 | 0 | { |
140 | 0 | case RedChannel: |
141 | 0 | case CyanChannel: |
142 | 0 | { |
143 | 0 | for (i=0; i < npixels; i++) |
144 | 0 | { |
145 | 0 | pixels[i].green=pixels[i].red; |
146 | 0 | pixels[i].blue=pixels[i].red; |
147 | 0 | pixels[i].opacity=OpaqueOpacity; |
148 | 0 | } |
149 | 0 | break; |
150 | 0 | } |
151 | 0 | case GreenChannel: |
152 | 0 | case MagentaChannel: |
153 | 0 | { |
154 | 0 | for (i=0; i < npixels; i++) |
155 | 0 | { |
156 | 0 | pixels[i].red=pixels[i].green; |
157 | 0 | pixels[i].blue=pixels[i].green; |
158 | 0 | pixels[i].opacity=OpaqueOpacity; |
159 | 0 | } |
160 | 0 | break; |
161 | 0 | } |
162 | 0 | case BlueChannel: |
163 | 0 | case YellowChannel: |
164 | 0 | { |
165 | 0 | for (i=0; i < npixels; i++) |
166 | 0 | { |
167 | 0 | pixels[i].red=pixels[i].blue; |
168 | 0 | pixels[i].green=pixels[i].blue; |
169 | 0 | pixels[i].opacity=OpaqueOpacity; |
170 | 0 | } |
171 | 0 | break; |
172 | 0 | } |
173 | 0 | case MatteChannel: |
174 | 0 | case OpacityChannel: |
175 | 0 | { |
176 | 0 | if (image->colorspace == CMYKColorspace) |
177 | 0 | { |
178 | 0 | for (i=0; i < npixels; i++) |
179 | 0 | { |
180 | 0 | pixels[i].red=indexes[i]; |
181 | 0 | pixels[i].green=indexes[i]; |
182 | 0 | pixels[i].blue=indexes[i]; |
183 | 0 | pixels[i].opacity=OpaqueOpacity; |
184 | 0 | } |
185 | 0 | } |
186 | 0 | else |
187 | 0 | { |
188 | 0 | for (i=0; i < npixels; i++) |
189 | 0 | { |
190 | 0 | pixels[i].red=pixels[i].opacity; |
191 | 0 | pixels[i].green=pixels[i].opacity; |
192 | 0 | pixels[i].blue=pixels[i].opacity; |
193 | 0 | pixels[i].opacity=OpaqueOpacity; |
194 | 0 | } |
195 | 0 | } |
196 | 0 | image->matte=False; |
197 | 0 | break; |
198 | 0 | } |
199 | 0 | case BlackChannel: |
200 | 0 | { |
201 | 0 | for (i=0; i < npixels; i++) |
202 | 0 | { |
203 | 0 | pixels[i].red=pixels[i].opacity; |
204 | 0 | pixels[i].green=pixels[i].opacity; |
205 | 0 | pixels[i].blue=pixels[i].opacity; |
206 | 0 | pixels[i].opacity=OpaqueOpacity; |
207 | 0 | } |
208 | 0 | image->matte=False; |
209 | 0 | break; |
210 | 0 | } |
211 | | |
212 | 0 | case UndefinedChannel: |
213 | 0 | case AllChannels: |
214 | 0 | case GrayChannel: |
215 | 0 | { |
216 | 0 | for (i=0; i < npixels; i++) |
217 | 0 | { |
218 | 0 | pixels[i].red=pixels[i].green=pixels[i].blue=PixelIntensity(&pixels[i]); |
219 | 0 | pixels[i].opacity=OpaqueOpacity; |
220 | 0 | } |
221 | 0 | image->matte=False; |
222 | 0 | break; |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | 0 | return MagickPass; |
227 | 0 | } |
228 | | MagickExport MagickPassFail ChannelImage(Image *image,const ChannelType channel) |
229 | 0 | { |
230 | 0 | char |
231 | 0 | progress_message[MaxTextExtent]; |
232 | |
|
233 | 0 | ChannelType |
234 | 0 | channel_type = channel; |
235 | |
|
236 | 0 | MagickPassFail |
237 | 0 | status=MagickPass; |
238 | | |
239 | | /* |
240 | | Channel DirectClass packets. |
241 | | */ |
242 | 0 | assert(image != (Image *) NULL); |
243 | 0 | assert(image->signature == MagickSignature); |
244 | |
|
245 | 0 | MagickFormatString(progress_message,sizeof(progress_message), |
246 | 0 | "[%%s] Extract %s channel... ", |
247 | 0 | ChannelTypeToString(channel)); |
248 | | |
249 | | /* |
250 | | Verify that image colorspace is compatible with with requested |
251 | | channel type. |
252 | | */ |
253 | 0 | if (ValidateChannelRequest(image->colorspace,channel,&image->exception) |
254 | 0 | == MagickFail) |
255 | 0 | return MagickFail; |
256 | | |
257 | 0 | image->storage_class=DirectClass; |
258 | 0 | status=PixelIterateMonoModify(ChannelImagePixels, |
259 | 0 | NULL, |
260 | 0 | progress_message, |
261 | 0 | NULL,&channel_type,0,0,image->columns,image->rows, |
262 | 0 | image,&image->exception); |
263 | |
|
264 | 0 | image->matte=MagickFalse; |
265 | 0 | image->is_grayscale=MagickTrue; |
266 | 0 | image->colorspace=RGBColorspace; |
267 | |
|
268 | 0 | return(status); |
269 | 0 | } |
270 | | |
271 | | /* |
272 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
273 | | % % |
274 | | % % |
275 | | % % |
276 | | % E x p o r t I m a g e C h a n n e l % |
277 | | % % |
278 | | % % |
279 | | % % |
280 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
281 | | % |
282 | | % ExportImageChannel() exports a specified image channel as a new image. |
283 | | % |
284 | | % The format of the ExportImageChannel method is: |
285 | | % |
286 | | % Image *ExportImageChannel(const Image *image, |
287 | | % const ChannelType channel, |
288 | | % ExceptionInfo *exception) |
289 | | % |
290 | | % A description of each parameter follows: |
291 | | % |
292 | | % o image: The source image. |
293 | | % |
294 | | % o channel: The image channel to export |
295 | | % |
296 | | % o exception: Return any errors or warnings in this structure. |
297 | | % |
298 | | % |
299 | | */ |
300 | | #define EXPORT_CHANNEL(source) \ |
301 | 0 | do { \ |
302 | 0 | register long \ |
303 | 0 | i; \ |
304 | 0 | \ |
305 | 0 | if (source_image->storage_class == PseudoClass) \ |
306 | 0 | { \ |
307 | 0 | for (i=0; i < npixels; i++) \ |
308 | 0 | { \ |
309 | 0 | new_pixels[i].red=new_pixels[i].green=new_pixels[i].blue= \ |
310 | 0 | source_image->colormap[source_indexes[i]].source; \ |
311 | 0 | new_pixels[i].opacity=OpaqueOpacity; \ |
312 | 0 | } \ |
313 | 0 | } \ |
314 | 0 | else \ |
315 | 0 | { \ |
316 | 0 | for (i=0; i < npixels; i++) \ |
317 | 0 | { \ |
318 | 0 | new_pixels[i].red=new_pixels[i].green=new_pixels[i].blue= \ |
319 | 0 | source_pixels[i].source; \ |
320 | 0 | new_pixels[i].opacity=OpaqueOpacity; \ |
321 | 0 | } \ |
322 | 0 | } \ |
323 | 0 | } while (0); |
324 | | |
325 | | static MagickPassFail |
326 | | ExportImageChannelPixels(void *mutable_data, /* User provided mutable data */ |
327 | | const void *immutable_data, /* User provided immutable data */ |
328 | | const Image * restrict source_image, /* Source image */ |
329 | | const PixelPacket * restrict source_pixels, /* Pixel row in source image */ |
330 | | const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */ |
331 | | Image * restrict new_image, /* New image */ |
332 | | PixelPacket * restrict new_pixels, /* Pixel row in new image */ |
333 | | IndexPacket * restrict new_indexes, /* Pixel row indexes in new image */ |
334 | | const long npixels, /* Number of pixels in row */ |
335 | | ExceptionInfo *exception /* Exception report */ |
336 | | ) |
337 | 0 | { |
338 | 0 | ChannelType |
339 | 0 | channel = *((const ChannelType *) immutable_data); |
340 | |
|
341 | 0 | ARG_NOT_USED(mutable_data); |
342 | 0 | ARG_NOT_USED(new_image); |
343 | 0 | ARG_NOT_USED(new_indexes); |
344 | 0 | ARG_NOT_USED(exception); |
345 | |
|
346 | 0 | switch (channel) |
347 | 0 | { |
348 | 0 | case RedChannel: |
349 | 0 | case CyanChannel: |
350 | 0 | { |
351 | 0 | EXPORT_CHANNEL(red); |
352 | 0 | break; |
353 | 0 | } |
354 | 0 | case GreenChannel: |
355 | 0 | case MagentaChannel: |
356 | 0 | { |
357 | 0 | EXPORT_CHANNEL(green); |
358 | 0 | break; |
359 | 0 | } |
360 | 0 | case BlueChannel: |
361 | 0 | case YellowChannel: |
362 | 0 | { |
363 | 0 | EXPORT_CHANNEL(blue); |
364 | 0 | break; |
365 | 0 | } |
366 | 0 | case MatteChannel: |
367 | 0 | case OpacityChannel: |
368 | 0 | { |
369 | 0 | if (source_image->colorspace == CMYKColorspace) |
370 | 0 | { |
371 | 0 | register long |
372 | 0 | i; |
373 | |
|
374 | 0 | for (i=0; i < npixels; i++) |
375 | 0 | { |
376 | 0 | new_pixels[i].red=new_pixels[i].green= |
377 | 0 | new_pixels[i].blue=source_indexes[i]; |
378 | 0 | new_pixels[i].opacity=OpaqueOpacity; |
379 | 0 | } |
380 | 0 | } |
381 | 0 | else |
382 | 0 | { |
383 | 0 | EXPORT_CHANNEL(opacity); |
384 | 0 | } |
385 | 0 | break; |
386 | 0 | } |
387 | 0 | case BlackChannel: |
388 | 0 | { |
389 | 0 | EXPORT_CHANNEL(opacity); |
390 | 0 | break; |
391 | 0 | } |
392 | 0 | default: |
393 | 0 | { |
394 | 0 | } |
395 | 0 | } |
396 | | |
397 | 0 | return MagickPass; |
398 | 0 | } |
399 | 0 | #define ExportImageChannelText "[%s] Exporting channel... " |
400 | | MagickExport Image *ExportImageChannel(const Image *source_image, |
401 | | const ChannelType channel, |
402 | | ExceptionInfo *exception) |
403 | 0 | { |
404 | 0 | ChannelType |
405 | 0 | channel_type = channel; |
406 | |
|
407 | 0 | Image |
408 | 0 | *new_image; |
409 | |
|
410 | 0 | assert(source_image != (Image *) NULL); |
411 | 0 | assert(source_image->signature == MagickSignature); |
412 | 0 | assert(exception != (ExceptionInfo *) NULL); |
413 | 0 | assert(exception->signature == MagickSignature); |
414 | | |
415 | | /* |
416 | | Verify that image colorspace is compatible with with requested |
417 | | channel type. |
418 | | */ |
419 | 0 | if (ValidateChannelRequest(source_image->colorspace,channel,exception) |
420 | 0 | == MagickFail) |
421 | 0 | return (Image *) NULL; |
422 | | |
423 | 0 | new_image=CloneImage(source_image,source_image->columns,source_image->rows, |
424 | 0 | True,exception); |
425 | 0 | if (new_image == (Image *) NULL) |
426 | 0 | return ((Image *) NULL); |
427 | | |
428 | 0 | new_image->storage_class=DirectClass; |
429 | |
|
430 | 0 | (void) PixelIterateDualNew(ExportImageChannelPixels, |
431 | 0 | NULL, |
432 | 0 | ExportImageChannelText, |
433 | 0 | NULL,&channel_type, |
434 | 0 | source_image->columns,source_image->rows, |
435 | 0 | source_image,0,0, |
436 | 0 | new_image,0,0, |
437 | 0 | exception); |
438 | |
|
439 | 0 | new_image->is_grayscale=True; |
440 | 0 | new_image->is_monochrome=source_image->is_monochrome; |
441 | 0 | return new_image; |
442 | 0 | } |
443 | | |
444 | | /* |
445 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
446 | | % % |
447 | | % % |
448 | | % % |
449 | | % G e t I m a g e C h a n n e l D e p t h % |
450 | | % % |
451 | | % % |
452 | | % % |
453 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
454 | | % |
455 | | % GetImageChannelDepth() returns the minimum bit depth required to store |
456 | | % the specified image channel without actual loss of color resolution. |
457 | | % Pixel components are stored in a Quantum, which is 8, 16, or 32 bits |
458 | | % depending on the QuantumDepth value set when the software is compiled. |
459 | | % GetImageChannelDepth() returns the smallest modulus storage size which |
460 | | % supports the scale of the pixel within the range (i.e. no information is |
461 | | % lost). As an example, the value one is returned for a bilevel channel |
462 | | % since only one bit of resolution is required to represent a bilevel channel. |
463 | | % |
464 | | % The format of the GetImageChannelDepth method is: |
465 | | % |
466 | | % unsigned long GetImageChannelDepth(const Image *image, |
467 | | % const ChannelType channel,ExceptionInfo *exception) |
468 | | % |
469 | | % A description of each parameter follows: |
470 | | % |
471 | | % o image: The image. |
472 | | % |
473 | | % o channel: Channel to test. |
474 | | % |
475 | | % o exception: Return any errors or warnings in this structure. |
476 | | % |
477 | | % |
478 | | */ |
479 | 0 | #define ComputeChannelDepthText "[%s] Get channel depth..." |
480 | | |
481 | | #define CHANNEL_DEPTH(parameter) \ |
482 | 0 | { \ |
483 | 0 | register long \ |
484 | 0 | i; \ |
485 | 0 | \ |
486 | 0 | register unsigned int \ |
487 | 0 | scale; \ |
488 | 0 | \ |
489 | 0 | if (depth < 1) \ |
490 | 0 | depth=1; \ |
491 | 0 | scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth)); \ |
492 | 0 | i=0; \ |
493 | 0 | while (i < npixels) \ |
494 | 0 | { \ |
495 | 0 | if ((parameter) != scale*((parameter)/scale)) \ |
496 | 0 | { \ |
497 | 0 | depth++; \ |
498 | 0 | if (depth == QuantumDepth) \ |
499 | 0 | break; \ |
500 | 0 | scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth)); \ |
501 | 0 | continue; \ |
502 | 0 | } \ |
503 | 0 | i++; \ |
504 | 0 | } \ |
505 | 0 | } |
506 | | |
507 | | static MagickPassFail |
508 | | GetImageChannelDepthPixels(void *mutable_data, /* User provided mutable data */ |
509 | | const void *immutable_data, /* User provided immutable data */ |
510 | | const Image * restrict image, /* Input image */ |
511 | | const PixelPacket * restrict pixels, /* Pixel row */ |
512 | | const IndexPacket * restrict indexes, /* Pixel indexes */ |
513 | | const long npixels, /* Number of pixels in row */ |
514 | | ExceptionInfo *exception /* Exception report */ |
515 | | ) |
516 | 0 | { |
517 | 0 | unsigned int |
518 | 0 | *channel_depth=(unsigned int *) mutable_data; |
519 | |
|
520 | 0 | ChannelType |
521 | 0 | channel = *((const ChannelType *) immutable_data); |
522 | |
|
523 | 0 | register unsigned int |
524 | 0 | depth; |
525 | |
|
526 | 0 | ARG_NOT_USED(exception); |
527 | |
|
528 | | #if defined(HAVE_OPENMP) |
529 | | # pragma omp critical (GM_GetImageChannelDepthPixels) |
530 | | #endif |
531 | 0 | { |
532 | 0 | depth=*channel_depth; |
533 | 0 | } |
534 | |
|
535 | 0 | switch (channel) |
536 | 0 | { |
537 | 0 | case RedChannel: |
538 | 0 | case CyanChannel: |
539 | 0 | { |
540 | 0 | CHANNEL_DEPTH(pixels[i].red); |
541 | 0 | break; |
542 | 0 | } |
543 | 0 | case GreenChannel: |
544 | 0 | case MagentaChannel: |
545 | 0 | { |
546 | 0 | CHANNEL_DEPTH(pixels[i].green); |
547 | 0 | break; |
548 | 0 | } |
549 | 0 | case BlueChannel: |
550 | 0 | case YellowChannel: |
551 | 0 | { |
552 | 0 | CHANNEL_DEPTH(pixels[i].blue); |
553 | 0 | break; |
554 | 0 | } |
555 | 0 | case MatteChannel: |
556 | 0 | case OpacityChannel: |
557 | 0 | { |
558 | 0 | if (image->colorspace == CMYKColorspace) |
559 | 0 | { |
560 | 0 | CHANNEL_DEPTH(indexes[i]); |
561 | 0 | } |
562 | 0 | else |
563 | 0 | { |
564 | 0 | CHANNEL_DEPTH(pixels[i].opacity); |
565 | 0 | } |
566 | 0 | break; |
567 | 0 | } |
568 | 0 | case BlackChannel: |
569 | 0 | { |
570 | 0 | CHANNEL_DEPTH(pixels[i].opacity); |
571 | 0 | break; |
572 | 0 | } |
573 | 0 | default: |
574 | 0 | { |
575 | 0 | } |
576 | 0 | } |
577 | | |
578 | | #if defined(HAVE_OPENMP) |
579 | | # pragma omp critical (GM_GetImageChannelDepthPixels) |
580 | | #endif |
581 | 0 | { |
582 | 0 | if (depth > *channel_depth) |
583 | 0 | *channel_depth=depth; |
584 | 0 | } |
585 | |
|
586 | 0 | if (depth >= QuantumDepth) |
587 | 0 | return MagickFail; |
588 | | |
589 | 0 | return MagickPass; |
590 | 0 | } |
591 | | |
592 | | MagickExport unsigned int |
593 | | GetImageChannelDepth(const Image *image, |
594 | | const ChannelType channel, |
595 | | ExceptionInfo *exception) |
596 | 0 | { |
597 | 0 | unsigned int |
598 | 0 | depth; |
599 | |
|
600 | 0 | assert(image != (Image *) NULL); |
601 | 0 | assert(image->signature == MagickSignature); |
602 | |
|
603 | 0 | depth=1; |
604 | |
|
605 | 0 | (void) PixelIterateMonoRead(GetImageChannelDepthPixels, |
606 | 0 | NULL, |
607 | 0 | ComputeChannelDepthText, |
608 | 0 | &depth, |
609 | 0 | &channel, |
610 | 0 | 0,0,image->columns,image->rows, |
611 | 0 | image,exception); |
612 | 0 | return depth; |
613 | 0 | } |
614 | | |
615 | | /* |
616 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
617 | | % % |
618 | | % % |
619 | | % % |
620 | | % I m p o r t I m a g e C h a n n e l % |
621 | | % % |
622 | | % % |
623 | | % % |
624 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
625 | | % |
626 | | % ImportImageChannel() imports an image into the specified image channel. |
627 | | % |
628 | | % The format of the ImportImageChannel method is: |
629 | | % |
630 | | % MagickPassFail ImportImageChannel(const Image *source_image, |
631 | | % Image *update_image, |
632 | | % const ChannelType channel) |
633 | | % |
634 | | % A description of each parameter follows: |
635 | | % |
636 | | % o source_image: The image to use as the replacement image channel. |
637 | | % |
638 | | % o update_image: The image to import the channel into. |
639 | | % |
640 | | % o channel: The image channel to import |
641 | | % |
642 | | % |
643 | | */ |
644 | | #define IMPORT_CHANNEL(target) \ |
645 | 0 | do \ |
646 | 0 | { \ |
647 | 0 | register long \ |
648 | 0 | i; \ |
649 | 0 | \ |
650 | 0 | if (source_image->storage_class == PseudoClass) \ |
651 | 0 | { \ |
652 | 0 | if (source_image->is_grayscale) \ |
653 | 0 | for (i=0; i < npixels; i++) \ |
654 | 0 | target=source_image->colormap[source_indexes[i]].red; \ |
655 | 0 | else \ |
656 | 0 | for (i=0; i < npixels; i++) \ |
657 | 0 | target=PixelIntensityToQuantum(&source_image->colormap[source_indexes[i]]); \ |
658 | 0 | } \ |
659 | 0 | else \ |
660 | 0 | { \ |
661 | 0 | if (source_image->is_grayscale) \ |
662 | 0 | for (i=0; i < npixels; i++) \ |
663 | 0 | target=source_pixels[i].red; \ |
664 | 0 | else \ |
665 | 0 | for (i=0; i < npixels; i++) \ |
666 | 0 | target=PixelIntensityToQuantum(&source_pixels[i]); \ |
667 | 0 | } \ |
668 | 0 | } while (0); |
669 | | |
670 | | static MagickPassFail |
671 | | ImportImageChannelPixels(void *mutable_data, /* User provided mutable data */ |
672 | | const void *immutable_data, /* User provided immutable data */ |
673 | | const Image * restrict source_image, /* Source image */ |
674 | | const PixelPacket * restrict source_pixels, /* Pixel row in source image */ |
675 | | const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */ |
676 | | Image * restrict update_image, /* Update image */ |
677 | | PixelPacket * restrict update_pixels, /* Pixel row in update image */ |
678 | | IndexPacket * restrict update_indexes, /* Pixel row indexes in update image */ |
679 | | const long npixels, /* Number of pixels in row */ |
680 | | ExceptionInfo *exception /* Exception report */ |
681 | | ) |
682 | 0 | { |
683 | 0 | ChannelType |
684 | 0 | channel = *((const ChannelType *) immutable_data); |
685 | |
|
686 | 0 | ARG_NOT_USED(mutable_data); |
687 | 0 | ARG_NOT_USED(exception); |
688 | |
|
689 | 0 | switch (channel) |
690 | 0 | { |
691 | 0 | case RedChannel: |
692 | 0 | case CyanChannel: |
693 | 0 | { |
694 | 0 | IMPORT_CHANNEL(update_pixels[i].red); |
695 | 0 | break; |
696 | 0 | } |
697 | 0 | case GreenChannel: |
698 | 0 | case MagentaChannel: |
699 | 0 | { |
700 | 0 | IMPORT_CHANNEL(update_pixels[i].green); |
701 | 0 | break; |
702 | 0 | } |
703 | 0 | case BlueChannel: |
704 | 0 | case YellowChannel: |
705 | 0 | { |
706 | 0 | IMPORT_CHANNEL(update_pixels[i].blue); |
707 | 0 | break; |
708 | 0 | } |
709 | 0 | case MatteChannel: |
710 | 0 | case OpacityChannel: |
711 | 0 | { |
712 | 0 | if (update_image->colorspace == CMYKColorspace) |
713 | 0 | { |
714 | 0 | IMPORT_CHANNEL(update_indexes[i]); |
715 | 0 | } |
716 | 0 | else |
717 | 0 | { |
718 | 0 | IMPORT_CHANNEL(update_pixels[i].opacity); |
719 | 0 | } |
720 | 0 | break; |
721 | 0 | } |
722 | 0 | case BlackChannel: |
723 | 0 | { |
724 | 0 | IMPORT_CHANNEL(update_pixels[i].opacity); |
725 | 0 | break; |
726 | 0 | } |
727 | 0 | default: |
728 | 0 | { |
729 | 0 | } |
730 | 0 | } |
731 | | |
732 | 0 | return MagickPass; |
733 | 0 | } |
734 | | |
735 | 0 | #define ImportImageChannelText "[%s] Importing channel..." |
736 | | MagickPassFail ImportImageChannel(const Image *source_image, |
737 | | Image *update_image, |
738 | | const ChannelType channel) |
739 | 0 | { |
740 | 0 | ChannelType |
741 | 0 | channel_type = channel; |
742 | |
|
743 | 0 | MagickPassFail |
744 | 0 | status=MagickPass; |
745 | |
|
746 | 0 | assert(update_image != (Image *) NULL); |
747 | 0 | assert(update_image->signature == MagickSignature); |
748 | 0 | assert(source_image != (Image *) NULL); |
749 | 0 | assert(source_image->signature == MagickSignature); |
750 | | |
751 | | /* |
752 | | Verify that image colorspace is compatible with with requested |
753 | | channel type. |
754 | | */ |
755 | 0 | if (ValidateChannelRequest(update_image->colorspace,channel,&update_image->exception) |
756 | 0 | == MagickFail) |
757 | 0 | return MagickFail; |
758 | | |
759 | 0 | update_image->storage_class=DirectClass; |
760 | 0 | status=PixelIterateDualModify(ImportImageChannelPixels, |
761 | 0 | NULL, |
762 | 0 | ImportImageChannelText, |
763 | 0 | NULL,&channel_type, |
764 | 0 | source_image->columns,source_image->rows, |
765 | 0 | source_image,0,0, |
766 | 0 | update_image,0,0, |
767 | 0 | &update_image->exception); |
768 | 0 | return(status); |
769 | 0 | } |
770 | | |
771 | | /* |
772 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
773 | | % % |
774 | | % % |
775 | | % % |
776 | | % I m p o r t I m a g e C h a n n e l s M a s k e d % |
777 | | % % |
778 | | % % |
779 | | % % |
780 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
781 | | % |
782 | | % ImportImageChannelsMasked() imports all the channels from a source |
783 | | % image to an update image, except for the channels specified. |
784 | | % |
785 | | % The format of the ImportImageChannelsMasked method is: |
786 | | % |
787 | | % MagickPassFail ImportImageChannelsMasked(const Image *source_image, |
788 | | % Image *update_image, |
789 | | % const ChannelType channels) |
790 | | % |
791 | | % A description of each parameter follows: |
792 | | % |
793 | | % o source_image: The image from which to extract the replacement channels. |
794 | | % |
795 | | % o update_image: The image to import the channels into. |
796 | | % |
797 | | % o channel: The image channel to import |
798 | | % |
799 | | % |
800 | | */ |
801 | | static MagickPassFail |
802 | | ImportImageChannelsMaskedPixels(void *mutable_data, /* User provided mutable data */ |
803 | | const void *immutable_data, /* User provided immutable data */ |
804 | | const Image * restrict source_image, /* Source image */ |
805 | | const PixelPacket * restrict source_pixels, /* Pixel row in source image */ |
806 | | const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */ |
807 | | Image * restrict update_image, /* Update image */ |
808 | | PixelPacket * restrict update_pixels, /* Pixel row in update image */ |
809 | | IndexPacket * restrict update_indexes, /* Pixel row indexes in update image */ |
810 | | const long npixels, /* Number of pixels in row */ |
811 | | ExceptionInfo *exception /* Exception report */ |
812 | | ) |
813 | 0 | { |
814 | 0 | ChannelType |
815 | 0 | channels = *((const ChannelType *) immutable_data); |
816 | |
|
817 | 0 | register long |
818 | 0 | i; |
819 | |
|
820 | 0 | ARG_NOT_USED(mutable_data); |
821 | 0 | ARG_NOT_USED(source_image); |
822 | 0 | ARG_NOT_USED(exception); |
823 | |
|
824 | 0 | if (IsCMYKColorspace(update_image->colorspace)) |
825 | 0 | { |
826 | 0 | if (!MagickChannelEnabled(channels,CyanChannel)) |
827 | 0 | for (i=0 ; i < npixels; i++) |
828 | 0 | SetCyanSample(&update_pixels[i],GetCyanSample(&source_pixels[i])); |
829 | 0 | if (!MagickChannelEnabled(channels,MagentaChannel)) |
830 | 0 | for (i=0 ; i < npixels; i++) |
831 | 0 | SetMagentaSample(&update_pixels[i],GetMagentaSample(&source_pixels[i])); |
832 | 0 | if (!MagickChannelEnabled(channels,YellowChannel)) |
833 | 0 | for (i=0 ; i < npixels; i++) |
834 | 0 | SetYellowSample(&update_pixels[i],GetYellowSample(&source_pixels[i])); |
835 | 0 | if (!MagickChannelEnabled(channels,BlackChannel)) |
836 | 0 | for (i=0 ; i < npixels; i++) |
837 | 0 | SetBlackSample(&update_pixels[i],GetBlackSample(&source_pixels[i])); |
838 | 0 | if ((update_image->matte) && |
839 | 0 | (!MagickChannelEnabled(channels,OpacityChannel)) && |
840 | 0 | (source_indexes != (const IndexPacket *) NULL) && |
841 | 0 | (update_indexes != (IndexPacket *) NULL)) |
842 | 0 | (void) memcpy(update_indexes,source_indexes,npixels*sizeof(IndexPacket)); |
843 | 0 | } |
844 | 0 | else |
845 | 0 | { |
846 | 0 | if (!MagickChannelEnabled(channels,RedChannel)) |
847 | 0 | for (i=0 ; i < npixels; i++) |
848 | 0 | SetRedSample(&update_pixels[i],GetRedSample(&source_pixels[i])); |
849 | 0 | if (!MagickChannelEnabled(channels,GreenChannel)) |
850 | 0 | for (i=0 ; i < npixels; i++) |
851 | 0 | SetGreenSample(&update_pixels[i],GetGreenSample(&source_pixels[i])); |
852 | 0 | if (!MagickChannelEnabled(channels,BlueChannel)) |
853 | 0 | for (i=0 ; i < npixels; i++) |
854 | 0 | SetBlueSample(&update_pixels[i],GetBlueSample(&source_pixels[i])); |
855 | 0 | if (!MagickChannelEnabled(channels,OpacityChannel)) |
856 | 0 | for (i=0 ; i < npixels; i++) |
857 | 0 | SetOpacitySample(&update_pixels[i],GetOpacitySample(&source_pixels[i])); |
858 | 0 | } |
859 | |
|
860 | 0 | return MagickPass; |
861 | 0 | } |
862 | | |
863 | 0 | #define ImportImageChannelsMaskedText "[%s] Importing channels... " |
864 | | MagickPassFail ImportImageChannelsMasked(const Image *source_image, |
865 | | Image *update_image, |
866 | | const ChannelType channels) |
867 | 0 | { |
868 | 0 | ChannelType |
869 | 0 | channel_type = channels; |
870 | |
|
871 | 0 | MagickPassFail |
872 | 0 | status=MagickPass; |
873 | |
|
874 | 0 | assert(update_image != (Image *) NULL); |
875 | 0 | assert(update_image->signature == MagickSignature); |
876 | 0 | assert(source_image != (Image *) NULL); |
877 | 0 | assert(source_image->signature == MagickSignature); |
878 | |
|
879 | 0 | if (!((AllChannels == channel_type) || (GrayChannel == channel_type))) |
880 | 0 | { |
881 | 0 | update_image->storage_class=DirectClass; |
882 | 0 | status=PixelIterateDualModify(ImportImageChannelsMaskedPixels, |
883 | 0 | NULL, |
884 | 0 | ImportImageChannelsMaskedText, |
885 | 0 | NULL,&channel_type, |
886 | 0 | source_image->columns,source_image->rows, |
887 | 0 | source_image,0,0, |
888 | 0 | update_image,0,0, |
889 | 0 | &update_image->exception); |
890 | 0 | } |
891 | 0 | return(status); |
892 | 0 | } |
893 | | |
894 | | /* |
895 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
896 | | % % |
897 | | % % |
898 | | % % |
899 | | % S e t I m a g e C h a n n e l D e p t h % |
900 | | % % |
901 | | % % |
902 | | % % |
903 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
904 | | % |
905 | | % SetImageChannelDepth() translates the pixel quantums in the specified |
906 | | % channel so that if they are later divided to fit within the specified bit |
907 | | % depth, that no additional information is lost (i.e. no remainder resulting |
908 | | % from the division). Note that any subsequent image processing is likely |
909 | | % to increase the effective depth of the image channels. A non-zero |
910 | | % value is returned if the operation is successful. Check the exception |
911 | | % member of image to determine the cause for any failure. |
912 | | % |
913 | | % The format of the SetImageChannelDepth method is: |
914 | | % |
915 | | % MagickPassFail SetImageChannelDepth(Image *image, |
916 | | % const ChannelType channel, |
917 | | % const unsigned int depth) |
918 | | % |
919 | | % A description of each parameter follows: |
920 | | % |
921 | | % o image: The image to update. |
922 | | % |
923 | | % o channel: Channel to modify. |
924 | | % |
925 | | % o depth: Desired channel depth (range 1 to QuantumDepth) |
926 | | % |
927 | | % |
928 | | */ |
929 | | MagickExport MagickPassFail SetImageChannelDepth(Image *image, |
930 | | const ChannelType channel, |
931 | | const unsigned int depth) |
932 | 0 | { |
933 | 0 | return QuantumOperatorImage(image,channel,DepthQuantumOp,(double) depth, |
934 | 0 | &image->exception); |
935 | 0 | } |