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