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