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