]> granicus.if.org Git - imagemagick/blob - coders/yuv.c
Fix improper cast that could cause an overflow as demonstrated in #347.
[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-2017 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/blob-private.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/colorspace.h"
47 #include "MagickCore/constitute.h"
48 #include "MagickCore/exception.h"
49 #include "MagickCore/exception-private.h"
50 #include "MagickCore/geometry.h"
51 #include "MagickCore/image.h"
52 #include "MagickCore/image-private.h"
53 #include "MagickCore/list.h"
54 #include "MagickCore/magick.h"
55 #include "MagickCore/memory_.h"
56 #include "MagickCore/monitor.h"
57 #include "MagickCore/monitor-private.h"
58 #include "MagickCore/pixel-accessor.h"
59 #include "MagickCore/resize.h"
60 #include "MagickCore/quantum-private.h"
61 #include "MagickCore/static.h"
62 #include "MagickCore/string_.h"
63 #include "MagickCore/module.h"
64 #include "MagickCore/utility.h"
65 \f
66 /*
67   Forward declarations.
68 */
69 static MagickBooleanType
70   WriteYUVImage(const ImageInfo *,Image *,ExceptionInfo *);
71 \f
72 /*
73 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74 %                                                                             %
75 %                                                                             %
76 %                                                                             %
77 %   R e a d Y U V I m a g e                                                   %
78 %                                                                             %
79 %                                                                             %
80 %                                                                             %
81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 %
83 %  ReadYUVImage() reads an image with digital YUV (CCIR 601 4:1:1, plane
84 %  or partition interlaced, or 4:2:2 plane, partition interlaced or
85 %  noninterlaced) bytes and returns it.  It allocates the memory necessary
86 %  for the new Image structure and returns a pointer to the new image.
87 %
88 %  The format of the ReadYUVImage method is:
89 %
90 %      Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
91 %
92 %  A description of each parameter follows:
93 %
94 %    o image_info: the image info.
95 %
96 %    o exception: return any errors or warnings in this structure.
97 %
98 */
99 static Image *ReadYUVImage(const ImageInfo *image_info,ExceptionInfo *exception)
100 {
101   Image
102     *chroma_image,
103     *image,
104     *resize_image;
105
106   InterlaceType
107     interlace;
108
109   MagickBooleanType
110     status;
111
112   register const Quantum
113     *chroma_pixels;
114
115   register ssize_t
116     x;
117
118   register Quantum
119     *q;
120
121   register unsigned char
122     *p;
123
124   ssize_t
125     count,
126     horizontal_factor,
127     quantum,
128     vertical_factor,
129     y;
130
131   unsigned char
132     *scanline;
133
134   /*
135     Allocate image structure.
136   */
137   assert(image_info != (const ImageInfo *) NULL);
138   assert(image_info->signature == 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 \f
467 /*
468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469 %                                                                             %
470 %                                                                             %
471 %                                                                             %
472 %   R e g i s t e r Y U V I m a g e                                           %
473 %                                                                             %
474 %                                                                             %
475 %                                                                             %
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477 %
478 %  RegisterYUVImage() adds attributes for the YUV image format to
479 %  the list of supported formats.  The attributes include the image format
480 %  tag, a method to read and/or write the format, whether the format
481 %  supports the saving of more than one frame to the same file or blob,
482 %  whether the format supports native in-memory I/O, and a brief
483 %  description of the format.
484 %
485 %  The format of the RegisterYUVImage method is:
486 %
487 %      size_t RegisterYUVImage(void)
488 %
489 */
490 ModuleExport size_t RegisterYUVImage(void)
491 {
492   MagickInfo
493     *entry;
494
495   entry=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 \f
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 */
523 ModuleExport void UnregisterYUVImage(void)
524 {
525   (void) UnregisterMagickInfo("YUV");
526 }
527 \f
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 */
557 static 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 }