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