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