]> 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-2011 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 "magick/studio.h"
43 #include "magick/blob.h"
44 #include "magick/blob-private.h"
45 #include "magick/cache.h"
46 #include "magick/colorspace.h"
47 #include "magick/constitute.h"
48 #include "magick/exception.h"
49 #include "magick/exception-private.h"
50 #include "magick/geometry.h"
51 #include "magick/image.h"
52 #include "magick/image-private.h"
53 #include "magick/list.h"
54 #include "magick/magick.h"
55 #include "magick/memory_.h"
56 #include "magick/monitor.h"
57 #include "magick/monitor-private.h"
58 #include "magick/resize.h"
59 #include "magick/quantum-private.h"
60 #include "magick/static.h"
61 #include "magick/string_.h"
62 #include "magick/module.h"
63 #include "magick/utility.h"
64 \f
65 /*
66   Forward declarations.
67 */
68 static MagickBooleanType
69   WriteYUVImage(const ImageInfo *,Image *);
70 \f
71 /*
72 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73 %                                                                             %
74 %                                                                             %
75 %                                                                             %
76 %   R e a d Y U V I m a g e                                                   %
77 %                                                                             %
78 %                                                                             %
79 %                                                                             %
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 %
82 %  ReadYUVImage() reads an image with digital YUV (CCIR 601 4:1:1, plane
83 %  or partition interlaced, or 4:2:2 plane, partition interlaced or
84 %  noninterlaced) bytes and returns it.  It allocates the memory necessary
85 %  for the new Image structure and returns a pointer to the new image.
86 %
87 %  The format of the ReadYUVImage method is:
88 %
89 %      Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
90 %
91 %  A description of each parameter follows:
92 %
93 %    o image_info: the image info.
94 %
95 %    o exception: return any errors or warnings in this structure.
96 %
97 */
98 static Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
99 {
100   Image
101     *chroma_image,
102     *image,
103     *resize_image;
104
105   InterlaceType
106     interlace;
107
108   ssize_t
109     horizontal_factor,
110     vertical_factor,
111     y;
112
113   MagickBooleanType
114     status;
115
116   register const PixelPacket
117     *chroma_pixels;
118
119   register ssize_t
120     x;
121
122   register PixelPacket
123     *q;
124
125   register unsigned char
126     *p;
127
128   ssize_t
129     quantum;
130
131   ssize_t
132     count;
133
134   unsigned char
135     *scanline;
136
137   /*
138     Allocate image structure.
139   */
140   assert(image_info != (const ImageInfo *) NULL);
141   assert(image_info->signature == MagickSignature);
142   if (image_info->debug != MagickFalse)
143     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
144       image_info->filename);
145   assert(exception != (ExceptionInfo *) NULL);
146   assert(exception->signature == MagickSignature);
147   image=AcquireImage(image_info);
148   if ((image->columns == 0) || (image->rows == 0))
149     ThrowReaderException(OptionError,"MustSpecifyImageSize");
150   quantum=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,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,quantum*sizeof(*scanline));
199   else
200     scanline=(unsigned char *) AcquireQuantumMemory((size_t) image->columns,
201       quantum*sizeof(*scanline));
202   if (scanline == (unsigned char *) NULL)
203     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
204   do
205   {
206     chroma_image=CloneImage(image,image->columns/horizontal_factor,
207       image->rows/vertical_factor,MagickTrue,exception);
208     if (chroma_image == (Image *) NULL)
209       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
210     /*
211       Convert raster image to pixel packets.
212     */
213     if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
214       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
215         break;
216     if (interlace == PartitionInterlace)
217       {
218         AppendImageFormat("Y",image->filename);
219         status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
220         if (status == MagickFalse)
221           {
222             image=DestroyImageList(image);
223             return((Image *) NULL);
224           }
225       }
226     for (y=0; y < (ssize_t) image->rows; y++)
227     {
228       register PixelPacket
229         *chroma_pixels;
230
231       if (interlace == NoInterlace)
232         {
233           if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
234             count=ReadBlob(image,(size_t) (2*quantum*image->columns),scanline);
235           p=scanline;
236           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
237           if (q == (PixelPacket *) NULL)
238             break;
239           chroma_pixels=QueueAuthenticPixels(chroma_image,0,y,
240             chroma_image->columns,1,exception);
241           if (chroma_pixels == (PixelPacket *) NULL)
242             break;
243           for (x=0; x < (ssize_t) image->columns; x+=2)
244           {
245             chroma_pixels->red=(Quantum) 0;
246             if (quantum == 1)
247               chroma_pixels->green=ScaleCharToQuantum(*p++);
248             else
249               {
250                 chroma_pixels->green=ScaleShortToQuantum(((*p) << 8) | *(p+1));
251                 p+=2;
252               }
253             if (quantum == 1)
254               q->red=ScaleCharToQuantum(*p++);
255             else
256               {
257                 q->red=ScaleShortToQuantum(((*p) << 8) | *(p+1));
258                 p+=2;
259               }
260             q->green=(Quantum) 0;
261             q->blue=(Quantum) 0;
262             q++;
263             q->green=0;
264             q->blue=0;
265             if (quantum == 1)
266               chroma_pixels->blue=ScaleCharToQuantum(*p++);
267             else
268               {
269                 chroma_pixels->blue=ScaleShortToQuantum(((*p) << 8) | *(p+1));
270                 p+=2;
271               }
272             if (quantum == 1)
273               q->red=ScaleCharToQuantum(*p++);
274             else
275               {
276                 q->red=ScaleShortToQuantum(((*p) << 8) | *(p+1));
277                 p+=2;
278               }
279             chroma_pixels++;
280             q++;
281           }
282         }
283       else
284         {
285           if ((y > 0) || (GetPreviousImageInList(image) == (Image *) NULL))
286             count=ReadBlob(image,(size_t) quantum*image->columns,scanline);
287           p=scanline;
288           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
289           if (q == (PixelPacket *) NULL)
290             break;
291           for (x=0; x < (ssize_t) image->columns; x++)
292           {
293             if (quantum == 1)
294               q->red=ScaleCharToQuantum(*p++);
295             else
296               {
297                 q->red=ScaleShortToQuantum(((*p) << 8) | *(p+1));
298                 p+=2;
299               }
300             q->green=0;
301             q->blue=0;
302             q++;
303           }
304         }
305       if (SyncAuthenticPixels(image,exception) == MagickFalse)
306         break;
307       if (interlace == NoInterlace)
308         if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
309           break;
310       if (image->previous == (Image *) NULL)
311         {
312           status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
313             image->rows);
314           if (status == MagickFalse)
315             break;
316         }
317     }
318     if (interlace == PartitionInterlace)
319       {
320         (void) CloseBlob(image);
321         AppendImageFormat("U",image->filename);
322         status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
323         if (status == MagickFalse)
324           {
325             image=DestroyImageList(image);
326             return((Image *) NULL);
327           }
328       }
329     if (interlace != NoInterlace)
330       {
331         for (y=0; y < (ssize_t) chroma_image->rows; y++)
332         {
333           count=ReadBlob(image,(size_t) quantum*chroma_image->columns,scanline);
334           p=scanline;
335           q=QueueAuthenticPixels(chroma_image,0,y,chroma_image->columns,1,
336             exception);
337           if (q == (PixelPacket *) NULL)
338             break;
339           for (x=0; x < (ssize_t) chroma_image->columns; x++)
340           {
341             q->red=(Quantum) 0;
342             if (quantum == 1)
343               q->green=ScaleCharToQuantum(*p++);
344             else
345               {
346                 q->green=ScaleShortToQuantum(((*p) << 8) | *(p+1));
347                 p+=2;
348               }
349             q->blue=(Quantum) 0;
350             q++;
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 == (PixelPacket *) NULL)
373           break;
374         for (x=0; x < (ssize_t) chroma_image->columns; x++)
375         {
376           if (quantum == 1)
377             q->blue=ScaleCharToQuantum(*p++);
378           else
379             {
380               q->blue=ScaleShortToQuantum(((*p) << 8) | *(p+1));
381               p+=2;
382             }
383           q++;
384         }
385         if (SyncAuthenticPixels(chroma_image,exception) == MagickFalse)
386           break;
387       }
388     }
389     /*
390       Scale image.
391     */
392     resize_image=ResizeImage(chroma_image,image->columns,image->rows,
393       TriangleFilter,1.0,exception);
394     chroma_image=DestroyImage(chroma_image);
395     if (resize_image == (Image *) NULL)
396       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
397     for (y=0; y < (ssize_t) image->rows; y++)
398     {
399       q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
400       chroma_pixels=GetVirtualPixels(resize_image,0,y,resize_image->columns,1,
401         &resize_image->exception);
402       if ((q == (PixelPacket *) NULL) ||
403           (chroma_pixels == (const PixelPacket *) NULL))
404         break;
405       for (x=0; x < (ssize_t) image->columns; x++)
406       {
407         q->green=chroma_pixels->green;
408         q->blue=chroma_pixels->blue;
409         chroma_pixels++;
410         q++;
411       }
412       if (SyncAuthenticPixels(image,exception) == MagickFalse)
413         break;
414     }
415     resize_image=DestroyImage(resize_image);
416     image->colorspace=YCbCrColorspace;
417     if (interlace == PartitionInterlace)
418       (void) CopyMagickString(image->filename,image_info->filename,
419         MaxTextExtent);
420     if (EOFBlob(image) != MagickFalse)
421       {
422         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
423           image->filename);
424         break;
425       }
426     /*
427       Proceed to next image.
428     */
429     if (image_info->number_scenes != 0)
430       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
431         break;
432     if (interlace == NoInterlace)
433       count=ReadBlob(image,(size_t) (2*quantum*image->columns),scanline);
434     else
435       count=ReadBlob(image,(size_t) quantum*image->columns,scanline);
436     if (count != 0)
437       {
438         /*
439           Allocate next image structure.
440         */
441         AcquireNextImage(image_info,image);
442         if (GetNextImageInList(image) == (Image *) NULL)
443           {
444             image=DestroyImageList(image);
445             return((Image *) NULL);
446           }
447         image=SyncNextImageInList(image);
448         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
449           GetBlobSize(image));
450         if (status == MagickFalse)
451           break;
452       }
453   } while (count != 0);
454   scanline=(unsigned char *) RelinquishMagickMemory(scanline);
455   (void) CloseBlob(image);
456   return(GetFirstImageInList(image));
457 }
458 \f
459 /*
460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461 %                                                                             %
462 %                                                                             %
463 %                                                                             %
464 %   R e g i s t e r Y U V I m a g e                                           %
465 %                                                                             %
466 %                                                                             %
467 %                                                                             %
468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469 %
470 %  RegisterYUVImage() adds attributes for the YUV image format to
471 %  the list of supported formats.  The attributes include the image format
472 %  tag, a method to read and/or write the format, whether the format
473 %  supports the saving of more than one frame to the same file or blob,
474 %  whether the format supports native in-memory I/O, and a brief
475 %  description of the format.
476 %
477 %  The format of the RegisterYUVImage method is:
478 %
479 %      size_t RegisterYUVImage(void)
480 %
481 */
482 ModuleExport size_t RegisterYUVImage(void)
483 {
484   MagickInfo
485     *entry;
486
487   entry=SetMagickInfo("YUV");
488   entry->decoder=(DecodeImageHandler *) ReadYUVImage;
489   entry->encoder=(EncodeImageHandler *) WriteYUVImage;
490   entry->adjoin=MagickFalse;
491   entry->raw=MagickTrue;
492   entry->description=ConstantString("CCIR 601 4:1:1 or 4:2:2");
493   entry->module=ConstantString("YUV");
494   (void) RegisterMagickInfo(entry);
495   return(MagickImageCoderSignature);
496 }
497 \f
498 /*
499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500 %                                                                             %
501 %                                                                             %
502 %                                                                             %
503 %   U n r e g i s t e r Y U V I m a g e                                       %
504 %                                                                             %
505 %                                                                             %
506 %                                                                             %
507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508 %
509 %  UnregisterYUVImage() removes format registrations made by the
510 %  YUV module from the list of supported formats.
511 %
512 %  The format of the UnregisterYUVImage method is:
513 %
514 %      UnregisterYUVImage(void)
515 %
516 */
517 ModuleExport void UnregisterYUVImage(void)
518 {
519   (void) UnregisterMagickInfo("YUV");
520 }
521 \f
522 /*
523 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
524 %                                                                             %
525 %                                                                             %
526 %                                                                             %
527 %   W r i t e Y U V I m a g e                                                 %
528 %                                                                             %
529 %                                                                             %
530 %                                                                             %
531 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
532 %
533 %  WriteYUVImage() writes an image to a file in the digital YUV
534 %  (CCIR 601 4:1:1, plane or partition interlaced, or 4:2:2 plane, partition
535 %  interlaced or noninterlaced) bytes and returns it.
536 %
537 %  The format of the WriteYUVImage method is:
538 %
539 %      MagickBooleanType WriteYUVImage(const ImageInfo *image_info,Image *image)
540 %
541 %  A description of each parameter follows.
542 %
543 %    o image_info: the image info.
544 %
545 %    o image:  The image.
546 %
547 */
548 static MagickBooleanType WriteYUVImage(const ImageInfo *image_info,Image *image)
549 {
550   Image
551     *chroma_image,
552     *yuv_image;
553
554   InterlaceType
555     interlace;
556
557   ssize_t
558     horizontal_factor,
559     vertical_factor,
560     y;
561
562   MagickBooleanType
563     status;
564
565   MagickOffsetType
566     scene;
567
568   register const PixelPacket
569     *p,
570     *s;
571
572   register ssize_t
573     x;
574
575   size_t
576     height,
577     quantum,
578     width;
579
580   assert(image_info != (const ImageInfo *) NULL);
581   assert(image_info->signature == MagickSignature);
582   assert(image != (Image *) NULL);
583   assert(image->signature == MagickSignature);
584   if (image->debug != MagickFalse)
585     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
586   quantum=image->depth <= 8 ? 1 : 2;
587   interlace=image->interlace;
588   horizontal_factor=2;
589   vertical_factor=2;
590   if (image_info->sampling_factor != (char *) NULL)
591     {
592       GeometryInfo
593         geometry_info;
594
595       MagickStatusType
596         flags;
597
598       flags=ParseGeometry(image_info->sampling_factor,&geometry_info);
599       horizontal_factor=(ssize_t) geometry_info.rho;
600       vertical_factor=(ssize_t) geometry_info.sigma;
601       if ((flags & SigmaValue) == 0)
602         vertical_factor=horizontal_factor;
603       if ((horizontal_factor != 1) && (horizontal_factor != 2) &&
604           (vertical_factor != 1) && (vertical_factor != 2))
605         ThrowWriterException(CorruptImageError,"UnexpectedSamplingFactor");
606     }
607   if ((interlace == UndefinedInterlace) ||
608       ((interlace == NoInterlace) && (vertical_factor == 2)))
609     {
610       interlace=NoInterlace;    /* CCIR 4:2:2 */
611       if (vertical_factor == 2)
612         interlace=PlaneInterlace; /* CCIR 4:1:1 */
613     }
614   if (interlace != PartitionInterlace)
615     {
616       /*
617         Open output image file.
618       */
619       status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
620       if (status == MagickFalse)
621         return(status);
622     }
623   else
624     {
625       AppendImageFormat("Y",image->filename);
626       status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
627       if (status == MagickFalse)
628         return(status);
629     }
630   scene=0;
631   do
632   {
633     /*
634       Sample image to an even width and height, if necessary.
635     */
636     image->depth=quantum == 1 ? 8 : 16;
637     width=image->columns+(image->columns & (horizontal_factor-1));
638     height=image->rows+(image->rows & (vertical_factor-1));
639     yuv_image=ResizeImage(image,width,height,TriangleFilter,1.0,
640       &image->exception);
641     if (yuv_image == (Image *) NULL)
642       ThrowWriterException(ResourceLimitError,image->exception.reason);
643     (void) TransformImageColorspace(yuv_image,YCbCrColorspace);
644     /*
645       Downsample image.
646     */
647     chroma_image=ResizeImage(image,width/horizontal_factor,
648       height/vertical_factor,TriangleFilter,1.0,&image->exception);
649     if (chroma_image == (Image *) NULL)
650       ThrowWriterException(ResourceLimitError,image->exception.reason);
651     (void) TransformImageColorspace(chroma_image,YCbCrColorspace);
652     if (interlace == NoInterlace)
653       {
654         /*
655           Write noninterlaced YUV.
656         */
657         for (y=0; y < (ssize_t) yuv_image->rows; y++)
658         {
659           p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,
660             &yuv_image->exception);
661           if (p == (const PixelPacket *) NULL)
662             break;
663           s=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
664             &chroma_image->exception);
665           if (s == (const PixelPacket *) NULL)
666             break;
667           for (x=0; x < (ssize_t) yuv_image->columns; x++)
668           {
669             if (quantum == 1)
670               {
671                 (void) WriteBlobByte(image,ScaleQuantumToChar(s->green));
672                 (void) WriteBlobByte(image,ScaleQuantumToChar(
673                   GetRedPixelComponent(p)));
674                 p++;
675                 (void) WriteBlobByte(image,ScaleQuantumToChar(s->blue));
676                 (void) WriteBlobByte(image,ScaleQuantumToChar(
677                   GetRedPixelComponent(p)));
678               }
679             else
680               {
681                 (void) WriteBlobShort(image,ScaleQuantumToShort(s->green));
682                 (void) WriteBlobShort(image,ScaleQuantumToShort(
683                   GetRedPixelComponent(p)));
684                 p++;
685                 (void) WriteBlobShort(image,ScaleQuantumToShort(s->blue));
686                 (void) WriteBlobShort(image,ScaleQuantumToShort(
687                   GetRedPixelComponent(p)));
688               }
689             p++;
690             s++;
691             x++;
692           }
693           if (image->previous == (Image *) NULL)
694             {
695               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
696                 image->rows);
697               if (status == MagickFalse)
698                 break;
699             }
700         }
701         yuv_image=DestroyImage(yuv_image);
702       }
703     else
704       {
705         /*
706           Initialize Y channel.
707         */
708         for (y=0; y < (ssize_t) yuv_image->rows; y++)
709         {
710           p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,
711             &yuv_image->exception);
712           if (p == (const PixelPacket *) NULL)
713             break;
714           for (x=0; x < (ssize_t) yuv_image->columns; x++)
715           {
716             if (quantum == 1)
717               (void) WriteBlobByte(image,ScaleQuantumToChar(
718                 GetRedPixelComponent(p)));
719             else
720               (void) WriteBlobShort(image,ScaleQuantumToShort(
721                 GetRedPixelComponent(p)));
722             p++;
723           }
724           if (image->previous == (Image *) NULL)
725             {
726               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
727                 image->rows);
728               if (status == MagickFalse)
729                 break;
730             }
731         }
732         yuv_image=DestroyImage(yuv_image);
733         if (image->previous == (Image *) NULL)
734           {
735             status=SetImageProgress(image,SaveImageTag,1,3);
736             if (status == MagickFalse)
737               break;
738           }
739         /*
740           Initialize U channel.
741         */
742         if (interlace == PartitionInterlace)
743           {
744             (void) CloseBlob(image);
745             AppendImageFormat("U",image->filename);
746             status=OpenBlob(image_info,image,WriteBinaryBlobMode,
747               &image->exception);
748             if (status == MagickFalse)
749               return(status);
750           }
751         for (y=0; y < (ssize_t) chroma_image->rows; y++)
752         {
753           p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
754             &chroma_image->exception);
755           if (p == (const PixelPacket *) NULL)
756             break;
757           for (x=0; x < (ssize_t) chroma_image->columns; x++)
758           {
759             if (quantum == 1)
760               (void) WriteBlobByte(image,ScaleQuantumToChar(
761                 GetGreenPixelComponent(p)));
762             else
763               (void) WriteBlobShort(image,ScaleQuantumToShort(
764                 GetGreenPixelComponent(p)));
765             p++;
766           }
767         }
768         if (image->previous == (Image *) NULL)
769           {
770             status=SetImageProgress(image,SaveImageTag,2,3);
771             if (status == MagickFalse)
772               break;
773           }
774         /*
775           Initialize V channel.
776         */
777         if (interlace == PartitionInterlace)
778           {
779             (void) CloseBlob(image);
780             AppendImageFormat("V",image->filename);
781             status=OpenBlob(image_info,image,WriteBinaryBlobMode,
782               &image->exception);
783             if (status == MagickFalse)
784               return(status);
785           }
786         for (y=0; y < (ssize_t) chroma_image->rows; y++)
787         {
788           p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
789             &chroma_image->exception);
790           if (p == (const PixelPacket *) NULL)
791             break;
792           for (x=0; x < (ssize_t) chroma_image->columns; x++)
793           {
794             if (quantum == 1)
795               (void) WriteBlobByte(image,ScaleQuantumToChar(
796                 GetBluePixelComponent(p)));
797             else
798               (void) WriteBlobShort(image,ScaleQuantumToShort(
799                 GetBluePixelComponent(p)));
800             p++;
801           }
802         }
803         if (image->previous == (Image *) NULL)
804           {
805             status=SetImageProgress(image,SaveImageTag,2,3);
806             if (status == MagickFalse)
807               break;
808           }
809       }
810     chroma_image=DestroyImage(chroma_image);
811     if (interlace == PartitionInterlace)
812       (void) CopyMagickString(image->filename,image_info->filename,
813         MaxTextExtent);
814     if (GetNextImageInList(image) == (Image *) NULL)
815       break;
816     image=SyncNextImageInList(image);
817     status=SetImageProgress(image,SaveImagesTag,scene++,
818       GetImageListLength(image));
819     if (status == MagickFalse)
820       break;
821   } while (image_info->adjoin != MagickFalse);
822   (void) CloseBlob(image);
823   return(MagickTrue);
824 }