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