1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % Y Y U U V V %
7 % Y Y U U V V %
8 % Y U U V V %
9 % Y U U V V %
10 % Y UUU V %
11 % %
12 % %
13 % Read/Write Raw CCIR 601 4:1:1 or 4:2:2 Image Format %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999-2021 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/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38
39 /*
40 Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/blob-private.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/colorspace.h"
47 #include "MagickCore/constitute.h"
48 #include "MagickCore/exception.h"
49 #include "MagickCore/exception-private.h"
50 #include "MagickCore/geometry.h"
51 #include "MagickCore/image.h"
52 #include "MagickCore/image-private.h"
53 #include "MagickCore/list.h"
54 #include "MagickCore/magick.h"
55 #include "MagickCore/memory_.h"
56 #include "MagickCore/monitor.h"
57 #include "MagickCore/monitor-private.h"
58 #include "MagickCore/pixel-accessor.h"
59 #include "MagickCore/resize.h"
60 #include "MagickCore/quantum-private.h"
61 #include "MagickCore/static.h"
62 #include "MagickCore/string_.h"
63 #include "MagickCore/module.h"
64 #include "MagickCore/utility.h"
65
66 /*
67 Forward declarations.
68 */
69 static MagickBooleanType
70 WriteYUVImage(const ImageInfo *,Image *,ExceptionInfo *);
71
72 /*
73 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74 % %
75 % %
76 % %
77 % R e a d Y U V I m a g e %
78 % %
79 % %
80 % %
81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 %
83 % ReadYUVImage() reads an image with digital YUV (CCIR 601 4:1:1, plane
84 % or partition interlaced, or 4:2:2 plane, partition interlaced or
85 % noninterlaced) bytes and returns it. It allocates the memory necessary
86 % for the new Image structure and returns a pointer to the new image.
87 %
88 % The format of the ReadYUVImage method is:
89 %
90 % Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
91 %
92 % A description of each parameter follows:
93 %
94 % o image_info: the image info.
95 %
96 % o exception: return any errors or warnings in this structure.
97 %
98 */
ReadYUVImage(const ImageInfo * image_info,ExceptionInfo * exception)99 static Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
100 {
101 Image
102 *chroma_image,
103 *image,
104 *resize_image;
105
106 InterlaceType
107 interlace;
108
109 MagickBooleanType
110 status;
111
112 ssize_t
113 x;
114
115 Quantum
116 *q;
117
118 unsigned char
119 *p;
120
121 ssize_t
122 count,
123 horizontal_factor,
124 vertical_factor,
125 y;
126
127 size_t
128 length,
129 quantum;
130
131 unsigned char
132 *scanline;
133
134 /*
135 Allocate image structure.
136 */
137 assert(image_info != (const ImageInfo *) NULL);
138 assert(image_info->signature == MagickCoreSignature);
139 if (image_info->debug != MagickFalse)
140 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
141 image_info->filename);
142 assert(exception != (ExceptionInfo *) NULL);
143 assert(exception->signature == MagickCoreSignature);
144 image=AcquireImage(image_info,exception);
145 if ((image->columns == 0) || (image->rows == 0))
146 ThrowReaderException(OptionError,"MustSpecifyImageSize");
147 status=SetImageExtent(image,image->columns,image->rows,exception);
148 if (status == MagickFalse)
149 return(DestroyImageList(image));
150 quantum=(ssize_t) (image->depth <= 8 ? 1 : 2);
151 interlace=image_info->interlace;
152 horizontal_factor=2;
153 vertical_factor=2;
154 if (image_info->sampling_factor != (char *) NULL)
155 {
156 GeometryInfo
157 geometry_info;
158
159 MagickStatusType
160 flags;
161
162 flags=ParseGeometry(image_info->sampling_factor,&geometry_info);
163 horizontal_factor=(ssize_t) geometry_info.rho;
164 vertical_factor=(ssize_t) geometry_info.sigma;
165 if ((flags & SigmaValue) == 0)
166 vertical_factor=horizontal_factor;
167 if ((horizontal_factor != 1) && (horizontal_factor != 2) &&
168 (vertical_factor != 1) && (vertical_factor != 2))
169 ThrowReaderException(CorruptImageError,"UnexpectedSamplingFactor");
170 }
171 if ((interlace == UndefinedInterlace) ||
172 ((interlace == NoInterlace) && (vertical_factor == 2)))
173 {
174 interlace=NoInterlace; /* CCIR 4:2:2 */
175 if (vertical_factor == 2)
176 interlace=PlaneInterlace; /* CCIR 4:1:1 */
177 }
178 if (interlace != PartitionInterlace)
179 {
180 /*
181 Open image file.
182 */
183 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
184 if (status == MagickFalse)
185 {
186 image=DestroyImageList(image);
187 return((Image *) NULL);
188 }
189 if (DiscardBlobBytes(image,(MagickSizeType) image->offset) == MagickFalse)
190 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
191 image->filename);
192 }
193 /*
194 Allocate memory for a scanline.
195 */
196 if (interlace == NoInterlace)
197 scanline=(unsigned char *) AcquireQuantumMemory((size_t) (2UL*
198 image->columns+2UL),(size_t) quantum*sizeof(*scanline));
199 else
200 scanline=(unsigned char *) AcquireQuantumMemory(image->columns,
201 (size_t) quantum*sizeof(*scanline));
202 if (scanline == (unsigned char *) NULL)
203 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
204 status=MagickTrue;
205 do
206 {
207 chroma_image=CloneImage(image,(image->columns+horizontal_factor-1)/
208 horizontal_factor,(image->rows+vertical_factor-1)/vertical_factor,
209 MagickTrue,exception);
210 if (chroma_image == (Image *) NULL)
211 {
212 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
213 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
214 }
215 /*
216 Convert raster image to pixel packets.
217 */
218 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
219 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
220 break;
221 status=SetImageExtent(image,image->columns,image->rows,exception);
222 if (status == MagickFalse)
223 break;
224 if (interlace == PartitionInterlace)
225 {
226 AppendImageFormat("Y",image->filename);
227 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
228 if (status == MagickFalse)
229 {
230 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
231 image=DestroyImageList(image);
232 return((Image *) NULL);
233 }
234 }
235 for (y=0; y < (ssize_t) image->rows; y++)
236 {
237 Quantum
238 *chroma_pixels;
239
240 if (interlace == NoInterlace)
241 {
242 if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
243 {
244 length=2*quantum*image->columns;
245 count=ReadBlob(image,length,scanline);
246 if (count != (ssize_t) length)
247 {
248 status=MagickFalse;
249 ThrowFileException(exception,CorruptImageError,
250 "UnexpectedEndOfFile",image->filename);
251 break;
252 }
253 }
254 p=scanline;
255 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
256 if (q == (Quantum *) NULL)
257 break;
258 chroma_pixels=QueueAuthenticPixels(chroma_image,0,y,
259 chroma_image->columns,1,exception);
260 if (chroma_pixels == (Quantum *) NULL)
261 break;
262 for (x=0; x < (ssize_t) image->columns; x+=2)
263 {
264 SetPixelRed(chroma_image,0,chroma_pixels);
265 if (quantum == 1)
266 SetPixelGreen(chroma_image,ScaleCharToQuantum(*p++),
267 chroma_pixels);
268 else
269 {
270 SetPixelGreen(chroma_image,ScaleShortToQuantum(((*p) << 8) |
271 *(p+1)),chroma_pixels);
272 p+=2;
273 }
274 if (quantum == 1)
275 SetPixelRed(image,ScaleCharToQuantum(*p++),q);
276 else
277 {
278 SetPixelRed(image,ScaleShortToQuantum(((*p) << 8) | *(p+1)),q);
279 p+=2;
280 }
281 SetPixelGreen(image,0,q);
282 SetPixelBlue(image,0,q);
283 q+=GetPixelChannels(image);
284 SetPixelGreen(image,0,q);
285 SetPixelBlue(image,0,q);
286 if (quantum == 1)
287 SetPixelBlue(chroma_image,ScaleCharToQuantum(*p++),chroma_pixels);
288 else
289 {
290 SetPixelBlue(chroma_image,ScaleShortToQuantum(((*p) << 8) |
291 *(p+1)),chroma_pixels);
292 p+=2;
293 }
294 if (quantum == 1)
295 SetPixelRed(image,ScaleCharToQuantum(*p++),q);
296 else
297 {
298 SetPixelRed(image,ScaleShortToQuantum(((*p) << 8) | *(p+1)),q);
299 p+=2;
300 }
301 chroma_pixels+=GetPixelChannels(chroma_image);
302 q+=GetPixelChannels(image);
303 }
304 }
305 else
306 {
307 if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
308 {
309 length=quantum*image->columns;
310 count=ReadBlob(image,length,scanline);
311 if (count != (ssize_t) length)
312 {
313 status=MagickFalse;
314 ThrowFileException(exception,CorruptImageError,
315 "UnexpectedEndOfFile",image->filename);
316 break;
317 }
318 }
319 p=scanline;
320 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
321 if (q == (Quantum *) NULL)
322 break;
323 for (x=0; x < (ssize_t) image->columns; x++)
324 {
325 if (quantum == 1)
326 SetPixelRed(image,ScaleCharToQuantum(*p++),q);
327 else
328 {
329 SetPixelRed(image,ScaleShortToQuantum(((*p) << 8) | *(p+1)),q);
330 p+=2;
331 }
332 SetPixelGreen(image,0,q);
333 SetPixelBlue(image,0,q);
334 q+=GetPixelChannels(image);
335 }
336 }
337 if (SyncAuthenticPixels(image,exception) == MagickFalse)
338 break;
339 if (interlace == NoInterlace)
340 if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
341 break;
342 if (image->previous == (Image *) NULL)
343 {
344 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
345 image->rows);
346 if (status == MagickFalse)
347 break;
348 }
349 }
350 if (interlace == PartitionInterlace)
351 {
352 (void) CloseBlob(image);
353 AppendImageFormat("U",image->filename);
354 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
355 if (status == MagickFalse)
356 {
357 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
358 image=DestroyImageList(image);
359 return((Image *) NULL);
360 }
361 }
362 if (interlace != NoInterlace)
363 {
364 for (y=0; y < (ssize_t) chroma_image->rows; y++)
365 {
366 length=quantum*chroma_image->columns;
367 count=ReadBlob(image,length,scanline);
368 if (count != (ssize_t) length)
369 {
370 status=MagickFalse;
371 ThrowFileException(exception,CorruptImageError,
372 "UnexpectedEndOfFile",image->filename);
373 break;
374 }
375 p=scanline;
376 q=QueueAuthenticPixels(chroma_image,0,y,chroma_image->columns,1,
377 exception);
378 if (q == (Quantum *) NULL)
379 break;
380 for (x=0; x < (ssize_t) chroma_image->columns; x++)
381 {
382 SetPixelRed(chroma_image,0,q);
383 if (quantum == 1)
384 SetPixelGreen(chroma_image,ScaleCharToQuantum(*p++),q);
385 else
386 {
387 SetPixelGreen(chroma_image,ScaleShortToQuantum(((*p) << 8) |
388 *(p+1)),q);
389 p+=2;
390 }
391 SetPixelBlue(chroma_image,0,q);
392 q+=GetPixelChannels(chroma_image);
393 }
394 if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
395 break;
396 }
397 if (interlace == PartitionInterlace)
398 {
399 (void) CloseBlob(image);
400 AppendImageFormat("V",image->filename);
401 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
402 if (status == MagickFalse)
403 {
404 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
405 image=DestroyImageList(image);
406 return((Image *) NULL);
407 }
408 }
409 for (y=0; y < (ssize_t) chroma_image->rows; y++)
410 {
411 length=quantum*chroma_image->columns;
412 count=ReadBlob(image,length,scanline);
413 if (count != (ssize_t) length)
414 {
415 status=MagickFalse;
416 ThrowFileException(exception,CorruptImageError,
417 "UnexpectedEndOfFile",image->filename);
418 break;
419 }
420 p=scanline;
421 q=GetAuthenticPixels(chroma_image,0,y,chroma_image->columns,1,
422 exception);
423 if (q == (Quantum *) NULL)
424 break;
425 for (x=0; x < (ssize_t) chroma_image->columns; x++)
426 {
427 if (quantum == 1)
428 SetPixelBlue(chroma_image,ScaleCharToQuantum(*p++),q);
429 else
430 {
431 SetPixelBlue(chroma_image,ScaleShortToQuantum(((*p) << 8) |
432 *(p+1)),q);
433 p+=2;
434 }
435 q+=GetPixelChannels(chroma_image);
436 }
437 if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
438 break;
439 }
440 }
441 /*
442 Scale image.
443 */
444 resize_image=ResizeImage(chroma_image,image->columns,image->rows,
445 TriangleFilter,exception);
446 chroma_image=DestroyImage(chroma_image);
447 if (resize_image == (Image *) NULL)
448 {
449 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
450 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
451 }
452 for (y=0; y < (ssize_t) image->rows; y++)
453 {
454 const Quantum
455 *chroma_pixels;
456
457 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
458 chroma_pixels=GetVirtualPixels(resize_image,0,y,resize_image->columns,1,
459 exception);
460 if ((q == (Quantum *) NULL) ||
461 (chroma_pixels == (const Quantum *) NULL))
462 break;
463 for (x=0; x < (ssize_t) image->columns; x++)
464 {
465 SetPixelGreen(image,GetPixelGreen(resize_image,chroma_pixels),q);
466 SetPixelBlue(image,GetPixelBlue(resize_image,chroma_pixels),q);
467 chroma_pixels+=GetPixelChannels(resize_image);
468 q+=GetPixelChannels(image);
469 }
470 if (SyncAuthenticPixels(image,exception) == MagickFalse)
471 break;
472 }
473 resize_image=DestroyImage(resize_image);
474 if (SetImageColorspace(image,YCbCrColorspace,exception) == MagickFalse)
475 break;
476 if (interlace == PartitionInterlace)
477 (void) CopyMagickString(image->filename,image_info->filename,
478 MagickPathExtent);
479 if (EOFBlob(image) != MagickFalse)
480 {
481 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
482 image->filename);
483 break;
484 }
485 /*
486 Proceed to next image.
487 */
488 if (image_info->number_scenes != 0)
489 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
490 break;
491 if (interlace == NoInterlace)
492 count=ReadBlob(image,(size_t) (2*quantum*image->columns),scanline);
493 else
494 count=ReadBlob(image,(size_t) quantum*image->columns,scanline);
495 if (count != 0)
496 {
497 /*
498 Allocate next image structure.
499 */
500 AcquireNextImage(image_info,image,exception);
501 if (GetNextImageInList(image) == (Image *) NULL)
502 {
503 status=MagickFalse;
504 break;
505 }
506 image=SyncNextImageInList(image);
507 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
508 GetBlobSize(image));
509 if (status == MagickFalse)
510 break;
511 }
512 } while (count != 0);
513 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
514 (void) CloseBlob(image);
515 if (status == MagickFalse)
516 return(DestroyImageList(image));
517 return(GetFirstImageInList(image));
518 }
519
520 /*
521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522 % %
523 % %
524 % %
525 % R e g i s t e r Y U V I m a g e %
526 % %
527 % %
528 % %
529 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530 %
531 % RegisterYUVImage() adds attributes for the YUV image format to
532 % the list of supported formats. The attributes include the image format
533 % tag, a method to read and/or write the format, whether the format
534 % supports the saving of more than one frame to the same file or blob,
535 % whether the format supports native in-memory I/O, and a brief
536 % description of the format.
537 %
538 % The format of the RegisterYUVImage method is:
539 %
540 % size_t RegisterYUVImage(void)
541 %
542 */
RegisterYUVImage(void)543 ModuleExport size_t RegisterYUVImage(void)
544 {
545 MagickInfo
546 *entry;
547
548 entry=AcquireMagickInfo("YUV","YUV","CCIR 601 4:1:1 or 4:2:2");
549 entry->decoder=(DecodeImageHandler *) ReadYUVImage;
550 entry->encoder=(EncodeImageHandler *) WriteYUVImage;
551 entry->flags^=CoderAdjoinFlag;
552 entry->flags|=CoderRawSupportFlag;
553 (void) RegisterMagickInfo(entry);
554 return(MagickImageCoderSignature);
555 }
556
557 /*
558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
559 % %
560 % %
561 % %
562 % U n r e g i s t e r Y U V I m a g e %
563 % %
564 % %
565 % %
566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567 %
568 % UnregisterYUVImage() removes format registrations made by the
569 % YUV module from the list of supported formats.
570 %
571 % The format of the UnregisterYUVImage method is:
572 %
573 % UnregisterYUVImage(void)
574 %
575 */
UnregisterYUVImage(void)576 ModuleExport void UnregisterYUVImage(void)
577 {
578 (void) UnregisterMagickInfo("YUV");
579 }
580
581 /*
582 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
583 % %
584 % %
585 % %
586 % W r i t e Y U V I m a g e %
587 % %
588 % %
589 % %
590 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
591 %
592 % WriteYUVImage() writes an image to a file in the digital YUV
593 % (CCIR 601 4:1:1, plane or partition interlaced, or 4:2:2 plane, partition
594 % interlaced or noninterlaced) bytes and returns it.
595 %
596 % The format of the WriteYUVImage method is:
597 %
598 % MagickBooleanType WriteYUVImage(const ImageInfo *image_info,
599 % Image *image,ExceptionInfo *exception)
600 %
601 % A description of each parameter follows.
602 %
603 % o image_info: the image info.
604 %
605 % o image: The image.
606 %
607 % o exception: return any errors or warnings in this structure.
608 %
609 */
WriteYUVImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)610 static MagickBooleanType WriteYUVImage(const ImageInfo *image_info,Image *image,
611 ExceptionInfo *exception)
612 {
613 Image
614 *chroma_image,
615 *yuv_image;
616
617 InterlaceType
618 interlace;
619
620 MagickBooleanType
621 status;
622
623 MagickOffsetType
624 scene;
625
626 const Quantum
627 *p,
628 *s;
629
630 ssize_t
631 x;
632
633 size_t
634 height,
635 imageListLength,
636 quantum,
637 width;
638
639 ssize_t
640 horizontal_factor,
641 vertical_factor,
642 y;
643
644 assert(image_info != (const ImageInfo *) NULL);
645 assert(image_info->signature == MagickCoreSignature);
646 assert(image != (Image *) NULL);
647 assert(image->signature == MagickCoreSignature);
648 if (image->debug != MagickFalse)
649 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
650 quantum=(size_t) (image->depth <= 8 ? 1 : 2);
651 interlace=image->interlace;
652 horizontal_factor=2;
653 vertical_factor=2;
654 if (image_info->sampling_factor != (char *) NULL)
655 {
656 GeometryInfo
657 geometry_info;
658
659 MagickStatusType
660 flags;
661
662 flags=ParseGeometry(image_info->sampling_factor,&geometry_info);
663 horizontal_factor=(ssize_t) geometry_info.rho;
664 vertical_factor=(ssize_t) geometry_info.sigma;
665 if ((flags & SigmaValue) == 0)
666 vertical_factor=horizontal_factor;
667 if ((horizontal_factor != 1) && (horizontal_factor != 2) &&
668 (vertical_factor != 1) && (vertical_factor != 2))
669 ThrowWriterException(CorruptImageError,"UnexpectedSamplingFactor");
670 }
671 if ((interlace == UndefinedInterlace) ||
672 ((interlace == NoInterlace) && (vertical_factor == 2)))
673 {
674 interlace=NoInterlace; /* CCIR 4:2:2 */
675 if (vertical_factor == 2)
676 interlace=PlaneInterlace; /* CCIR 4:1:1 */
677 }
678 if (interlace != PartitionInterlace)
679 {
680 /*
681 Open output image file.
682 */
683 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
684 if (status == MagickFalse)
685 return(status);
686 }
687 else
688 {
689 AppendImageFormat("Y",image->filename);
690 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
691 if (status == MagickFalse)
692 return(status);
693 }
694 scene=0;
695 imageListLength=GetImageListLength(image);
696 do
697 {
698 /*
699 Sample image to an even width and height, if necessary.
700 */
701 image->depth=(size_t) (quantum == 1 ? 8 : 16);
702 width=image->columns+(image->columns & (horizontal_factor-1));
703 height=image->rows+(image->rows & (vertical_factor-1));
704 yuv_image=ResizeImage(image,width,height,TriangleFilter,exception);
705 if (yuv_image == (Image *) NULL)
706 {
707 (void) CloseBlob(image);
708 return(MagickFalse);
709 }
710 (void) TransformImageColorspace(yuv_image,YCbCrColorspace,exception);
711 /*
712 Downsample image.
713 */
714 chroma_image=ResizeImage(image,width/horizontal_factor,
715 height/vertical_factor,TriangleFilter,exception);
716 if (chroma_image == (Image *) NULL)
717 {
718 (void) CloseBlob(image);
719 return(MagickFalse);
720 }
721 (void) TransformImageColorspace(chroma_image,YCbCrColorspace,exception);
722 if (interlace == NoInterlace)
723 {
724 /*
725 Write noninterlaced YUV.
726 */
727 for (y=0; y < (ssize_t) yuv_image->rows; y++)
728 {
729 p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,exception);
730 if (p == (const Quantum *) NULL)
731 break;
732 s=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
733 exception);
734 if (s == (const Quantum *) NULL)
735 break;
736 for (x=0; x < (ssize_t) yuv_image->columns; x+=2)
737 {
738 if (quantum == 1)
739 {
740 (void) WriteBlobByte(image,ScaleQuantumToChar(
741 GetPixelGreen(yuv_image,s)));
742 (void) WriteBlobByte(image,ScaleQuantumToChar(
743 GetPixelRed(yuv_image,p)));
744 p+=GetPixelChannels(yuv_image);
745 (void) WriteBlobByte(image,ScaleQuantumToChar(
746 GetPixelBlue(yuv_image,s)));
747 (void) WriteBlobByte(image,ScaleQuantumToChar(
748 GetPixelRed(yuv_image,p)));
749 }
750 else
751 {
752 (void) WriteBlobByte(image,ScaleQuantumToChar(
753 GetPixelGreen(yuv_image,s)));
754 (void) WriteBlobShort(image,ScaleQuantumToShort(
755 GetPixelRed(yuv_image,p)));
756 p+=GetPixelChannels(yuv_image);
757 (void) WriteBlobByte(image,ScaleQuantumToChar(
758 GetPixelBlue(yuv_image,s)));
759 (void) WriteBlobShort(image,ScaleQuantumToShort(
760 GetPixelRed(yuv_image,p)));
761 }
762 p+=GetPixelChannels(yuv_image);
763 s++;
764 }
765 if (image->previous == (Image *) NULL)
766 {
767 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
768 image->rows);
769 if (status == MagickFalse)
770 break;
771 }
772 }
773 yuv_image=DestroyImage(yuv_image);
774 }
775 else
776 {
777 /*
778 Initialize Y channel.
779 */
780 for (y=0; y < (ssize_t) yuv_image->rows; y++)
781 {
782 p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,exception);
783 if (p == (const Quantum *) NULL)
784 break;
785 for (x=0; x < (ssize_t) yuv_image->columns; x++)
786 {
787 if (quantum == 1)
788 (void) WriteBlobByte(image,ScaleQuantumToChar(
789 GetPixelRed(yuv_image,p)));
790 else
791 (void) WriteBlobShort(image,ScaleQuantumToShort(
792 GetPixelRed(yuv_image,p)));
793 p+=GetPixelChannels(yuv_image);
794 }
795 if (image->previous == (Image *) NULL)
796 {
797 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
798 image->rows);
799 if (status == MagickFalse)
800 break;
801 }
802 }
803 yuv_image=DestroyImage(yuv_image);
804 if (image->previous == (Image *) NULL)
805 {
806 status=SetImageProgress(image,SaveImageTag,1,3);
807 if (status == MagickFalse)
808 break;
809 }
810 /*
811 Initialize U channel.
812 */
813 if (interlace == PartitionInterlace)
814 {
815 (void) CloseBlob(image);
816 AppendImageFormat("U",image->filename);
817 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
818 if (status == MagickFalse)
819 return(status);
820 }
821 for (y=0; y < (ssize_t) chroma_image->rows; y++)
822 {
823 p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
824 exception);
825 if (p == (const Quantum *) NULL)
826 break;
827 for (x=0; x < (ssize_t) chroma_image->columns; x++)
828 {
829 if (quantum == 1)
830 (void) WriteBlobByte(image,ScaleQuantumToChar(
831 GetPixelGreen(chroma_image,p)));
832 else
833 (void) WriteBlobShort(image,ScaleQuantumToShort(
834 GetPixelGreen(chroma_image,p)));
835 p+=GetPixelChannels(chroma_image);
836 }
837 }
838 if (image->previous == (Image *) NULL)
839 {
840 status=SetImageProgress(image,SaveImageTag,2,3);
841 if (status == MagickFalse)
842 break;
843 }
844 /*
845 Initialize V channel.
846 */
847 if (interlace == PartitionInterlace)
848 {
849 (void) CloseBlob(image);
850 AppendImageFormat("V",image->filename);
851 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
852 if (status == MagickFalse)
853 return(status);
854 }
855 for (y=0; y < (ssize_t) chroma_image->rows; y++)
856 {
857 p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
858 exception);
859 if (p == (const Quantum *) NULL)
860 break;
861 for (x=0; x < (ssize_t) chroma_image->columns; x++)
862 {
863 if (quantum == 1)
864 (void) WriteBlobByte(image,ScaleQuantumToChar(
865 GetPixelBlue(chroma_image,p)));
866 else
867 (void) WriteBlobShort(image,ScaleQuantumToShort(
868 GetPixelBlue(chroma_image,p)));
869 p+=GetPixelChannels(chroma_image);
870 }
871 }
872 if (image->previous == (Image *) NULL)
873 {
874 status=SetImageProgress(image,SaveImageTag,2,3);
875 if (status == MagickFalse)
876 break;
877 }
878 }
879 chroma_image=DestroyImage(chroma_image);
880 if (interlace == PartitionInterlace)
881 (void) CopyMagickString(image->filename,image_info->filename,
882 MagickPathExtent);
883 if (GetNextImageInList(image) == (Image *) NULL)
884 break;
885 image=SyncNextImageInList(image);
886 status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
887 if (status == MagickFalse)
888 break;
889 } while (image_info->adjoin != MagickFalse);
890 (void) CloseBlob(image);
891 return(MagickTrue);
892 }
893