]> 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 "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 *);
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);
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 == (const 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 == (const 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 == (const 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 == (const 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,1.0,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         &resize_image->exception);
403       if ((q == (const 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     image->colorspace=YCbCrColorspace;
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);
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,Image *image)
541 %
542 %  A description of each parameter follows.
543 %
544 %    o image_info: the image info.
545 %
546 %    o image:  The image.
547 %
548 */
549 static MagickBooleanType WriteYUVImage(const ImageInfo *image_info,Image *image)
550 {
551   Image
552     *chroma_image,
553     *yuv_image;
554
555   InterlaceType
556     interlace;
557
558   MagickBooleanType
559     status;
560
561   MagickOffsetType
562     scene;
563
564   register const Quantum
565     *p,
566     *s;
567
568   register ssize_t
569     x;
570
571   size_t
572     height,
573     quantum,
574     width;
575
576   ssize_t
577     horizontal_factor,
578     vertical_factor,
579     y;
580
581   assert(image_info != (const ImageInfo *) NULL);
582   assert(image_info->signature == MagickSignature);
583   assert(image != (Image *) NULL);
584   assert(image->signature == MagickSignature);
585   if (image->debug != MagickFalse)
586     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
587   quantum=image->depth <= 8 ? 1 : 2;
588   interlace=image->interlace;
589   horizontal_factor=2;
590   vertical_factor=2;
591   if (image_info->sampling_factor != (char *) NULL)
592     {
593       GeometryInfo
594         geometry_info;
595
596       MagickStatusType
597         flags;
598
599       flags=ParseGeometry(image_info->sampling_factor,&geometry_info);
600       horizontal_factor=(ssize_t) geometry_info.rho;
601       vertical_factor=(ssize_t) geometry_info.sigma;
602       if ((flags & SigmaValue) == 0)
603         vertical_factor=horizontal_factor;
604       if ((horizontal_factor != 1) && (horizontal_factor != 2) &&
605           (vertical_factor != 1) && (vertical_factor != 2))
606         ThrowWriterException(CorruptImageError,"UnexpectedSamplingFactor");
607     }
608   if ((interlace == UndefinedInterlace) ||
609       ((interlace == NoInterlace) && (vertical_factor == 2)))
610     {
611       interlace=NoInterlace;    /* CCIR 4:2:2 */
612       if (vertical_factor == 2)
613         interlace=PlaneInterlace; /* CCIR 4:1:1 */
614     }
615   if (interlace != PartitionInterlace)
616     {
617       /*
618         Open output image file.
619       */
620       status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
621       if (status == MagickFalse)
622         return(status);
623     }
624   else
625     {
626       AppendImageFormat("Y",image->filename);
627       status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
628       if (status == MagickFalse)
629         return(status);
630     }
631   scene=0;
632   do
633   {
634     /*
635       Sample image to an even width and height, if necessary.
636     */
637     image->depth=quantum == 1 ? 8 : 16;
638     width=image->columns+(image->columns & (horizontal_factor-1));
639     height=image->rows+(image->rows & (vertical_factor-1));
640     yuv_image=ResizeImage(image,width,height,TriangleFilter,1.0,
641       &image->exception);
642     if (yuv_image == (Image *) NULL)
643       ThrowWriterException(ResourceLimitError,image->exception.reason);
644     (void) TransformImageColorspace(yuv_image,YCbCrColorspace);
645     /*
646       Downsample image.
647     */
648     chroma_image=ResizeImage(image,width/horizontal_factor,
649       height/vertical_factor,TriangleFilter,1.0,&image->exception);
650     if (chroma_image == (Image *) NULL)
651       ThrowWriterException(ResourceLimitError,image->exception.reason);
652     (void) TransformImageColorspace(chroma_image,YCbCrColorspace);
653     if (interlace == NoInterlace)
654       {
655         /*
656           Write noninterlaced YUV.
657         */
658         for (y=0; y < (ssize_t) yuv_image->rows; y++)
659         {
660           p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,
661             &yuv_image->exception);
662           if (p == (const Quantum *) NULL)
663             break;
664           s=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
665             &chroma_image->exception);
666           if (s == (const Quantum *) NULL)
667             break;
668           for (x=0; x < (ssize_t) yuv_image->columns; x++)
669           {
670             if (quantum == 1)
671               {
672                 (void) WriteBlobByte(image,ScaleQuantumToChar(
673                   GetPixelGreen(yuv_image,s)));
674                 (void) WriteBlobByte(image,ScaleQuantumToChar(
675                   GetPixelRed(yuv_image,p)));
676                 p+=GetPixelChannels(yuv_image);
677                 (void) WriteBlobByte(image,ScaleQuantumToChar(
678                   GetPixelBlue(yuv_image,s)));
679                 (void) WriteBlobByte(image,ScaleQuantumToChar(
680                   GetPixelRed(yuv_image,p)));
681               }
682             else
683               {
684                 (void) WriteBlobByte(image,ScaleQuantumToChar(
685                   GetPixelGreen(yuv_image,s)));
686                 (void) WriteBlobShort(image,ScaleQuantumToShort(
687                   GetPixelRed(yuv_image,p)));
688                 p+=GetPixelChannels(yuv_image);
689                 (void) WriteBlobByte(image,ScaleQuantumToChar(
690                   GetPixelBlue(yuv_image,s)));
691                 (void) WriteBlobShort(image,ScaleQuantumToShort(
692                   GetPixelRed(yuv_image,p)));
693               }
694             p+=GetPixelChannels(yuv_image);
695             s++;
696             x++;
697           }
698           if (image->previous == (Image *) NULL)
699             {
700               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
701                 image->rows);
702               if (status == MagickFalse)
703                 break;
704             }
705         }
706         yuv_image=DestroyImage(yuv_image);
707       }
708     else
709       {
710         /*
711           Initialize Y channel.
712         */
713         for (y=0; y < (ssize_t) yuv_image->rows; y++)
714         {
715           p=GetVirtualPixels(yuv_image,0,y,yuv_image->columns,1,
716             &yuv_image->exception);
717           if (p == (const Quantum *) NULL)
718             break;
719           for (x=0; x < (ssize_t) yuv_image->columns; x++)
720           {
721             if (quantum == 1)
722               (void) WriteBlobByte(image,ScaleQuantumToChar(
723                 GetPixelRed(yuv_image,p)));
724             else
725               (void) WriteBlobShort(image,ScaleQuantumToShort(
726                 GetPixelRed(yuv_image,p)));
727             p+=GetPixelChannels(yuv_image);
728           }
729           if (image->previous == (Image *) NULL)
730             {
731               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
732                 image->rows);
733               if (status == MagickFalse)
734                 break;
735             }
736         }
737         yuv_image=DestroyImage(yuv_image);
738         if (image->previous == (Image *) NULL)
739           {
740             status=SetImageProgress(image,SaveImageTag,1,3);
741             if (status == MagickFalse)
742               break;
743           }
744         /*
745           Initialize U channel.
746         */
747         if (interlace == PartitionInterlace)
748           {
749             (void) CloseBlob(image);
750             AppendImageFormat("U",image->filename);
751             status=OpenBlob(image_info,image,WriteBinaryBlobMode,
752               &image->exception);
753             if (status == MagickFalse)
754               return(status);
755           }
756         for (y=0; y < (ssize_t) chroma_image->rows; y++)
757         {
758           p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
759             &chroma_image->exception);
760           if (p == (const Quantum *) NULL)
761             break;
762           for (x=0; x < (ssize_t) chroma_image->columns; x++)
763           {
764             if (quantum == 1)
765               (void) WriteBlobByte(image,ScaleQuantumToChar(
766                 GetPixelGreen(chroma_image,p)));
767             else
768               (void) WriteBlobShort(image,ScaleQuantumToShort(
769                 GetPixelGreen(chroma_image,p)));
770             p+=GetPixelChannels(chroma_image);
771           }
772         }
773         if (image->previous == (Image *) NULL)
774           {
775             status=SetImageProgress(image,SaveImageTag,2,3);
776             if (status == MagickFalse)
777               break;
778           }
779         /*
780           Initialize V channel.
781         */
782         if (interlace == PartitionInterlace)
783           {
784             (void) CloseBlob(image);
785             AppendImageFormat("V",image->filename);
786             status=OpenBlob(image_info,image,WriteBinaryBlobMode,
787               &image->exception);
788             if (status == MagickFalse)
789               return(status);
790           }
791         for (y=0; y < (ssize_t) chroma_image->rows; y++)
792         {
793           p=GetVirtualPixels(chroma_image,0,y,chroma_image->columns,1,
794             &chroma_image->exception);
795           if (p == (const Quantum *) NULL)
796             break;
797           for (x=0; x < (ssize_t) chroma_image->columns; x++)
798           {
799             if (quantum == 1)
800               (void) WriteBlobByte(image,ScaleQuantumToChar(
801                 GetPixelBlue(chroma_image,p)));
802             else
803               (void) WriteBlobShort(image,ScaleQuantumToShort(
804                 GetPixelBlue(chroma_image,p)));
805             p+=GetPixelChannels(chroma_image);
806           }
807         }
808         if (image->previous == (Image *) NULL)
809           {
810             status=SetImageProgress(image,SaveImageTag,2,3);
811             if (status == MagickFalse)
812               break;
813           }
814       }
815     chroma_image=DestroyImage(chroma_image);
816     if (interlace == PartitionInterlace)
817       (void) CopyMagickString(image->filename,image_info->filename,
818         MaxTextExtent);
819     if (GetNextImageInList(image) == (Image *) NULL)
820       break;
821     image=SyncNextImageInList(image);
822     status=SetImageProgress(image,SaveImagesTag,scene++,
823       GetImageListLength(image));
824     if (status == MagickFalse)
825       break;
826   } while (image_info->adjoin != MagickFalse);
827   (void) CloseBlob(image);
828   return(MagickTrue);
829 }