1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                     Y   Y   YYYC  BBBB    YYYC  RRRR                        %
7 %                      Y Y   C      B   B  C      R   R                       %
8 %                       Y    C      BBBB   C      RRRR                        %
9 %                       Y    C      B   B  C      R  R                        %
10 %                       Y     YYYC  BBBB    YYYC  R   R                       %
11 %                                                                             %
12 %                                                                             %
13 %                     Read/Write Raw YCbCr Image Format                       %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://imagemagick.org/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 
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/channel.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/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/quantum-private.h"
60 #include "MagickCore/static.h"
61 #include "MagickCore/statistic.h"
62 #include "MagickCore/string_.h"
63 #include "MagickCore/module.h"
64 #include "MagickCore/utility.h"
65 
66 /*
67   Forward declarations.
68 */
69 static MagickBooleanType
70   WriteYCBCRImage(const ImageInfo *,Image *,ExceptionInfo *);
71 
72 /*
73 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74 %                                                                             %
75 %                                                                             %
76 %                                                                             %
77 %   R e a d Y C b C r I m a g e                                               %
78 %                                                                             %
79 %                                                                             %
80 %                                                                             %
81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 %
83 %  ReadYCBCRImage() reads an image of raw YCbCr or YCbCrA samples and returns
84 %  it. It allocates the memory necessary for the new Image structure and
85 %  returns a pointer to the new image.
86 %
87 %  The format of the ReadYCBCRImage method is:
88 %
89 %      Image *ReadYCBCRImage(const ImageInfo *image_info,
90 %        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 */
ReadYCBCRImage(const ImageInfo * image_info,ExceptionInfo * exception)99 static Image *ReadYCBCRImage(const ImageInfo *image_info,
100   ExceptionInfo *exception)
101 {
102   const void
103     *stream;
104 
105   Image
106     *canvas_image,
107     *image;
108 
109   MagickBooleanType
110     status;
111 
112   MagickOffsetType
113     scene;
114 
115   QuantumInfo
116     *quantum_info;
117 
118   QuantumType
119     quantum_type;
120 
121   ssize_t
122     i;
123 
124   size_t
125     length;
126 
127   ssize_t
128     count,
129     y;
130 
131   unsigned char
132     *pixels;
133 
134   /*
135     Open image file.
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   (void) SetImageColorspace(image,YCbCrColorspace,exception);
151   if (image_info->interlace != PartitionInterlace)
152     {
153       status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
154       if (status == MagickFalse)
155         {
156           image=DestroyImageList(image);
157           return((Image *) NULL);
158         }
159       if (DiscardBlobBytes(image,(MagickSizeType) image->offset) == MagickFalse)
160         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
161           image->filename);
162     }
163   /*
164     Create virtual canvas to support cropping (i.e. image.rgb[100x100+10+20]).
165   */
166   canvas_image=CloneImage(image,image->extract_info.width,1,MagickFalse,
167     exception);
168   if (canvas_image == (Image *) NULL)
169     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
170   (void) SetImageVirtualPixelMethod(canvas_image,BlackVirtualPixelMethod,
171     exception);
172   quantum_info=AcquireQuantumInfo(image_info,canvas_image);
173   if (quantum_info == (QuantumInfo *) NULL)
174     {
175       canvas_image=DestroyImage(canvas_image);
176       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
177     }
178   quantum_type=RGBQuantum;
179   if (LocaleCompare(image_info->magick,"YCbCrA") == 0)
180     {
181       quantum_type=RGBAQuantum;
182       image->alpha_trait=BlendPixelTrait;
183     }
184   pixels=GetQuantumPixels(quantum_info);
185   if (image_info->number_scenes != 0)
186     while (image->scene < image_info->scene)
187     {
188       /*
189         Skip to next image.
190       */
191       image->scene++;
192       length=GetQuantumExtent(canvas_image,quantum_info,quantum_type);
193       for (y=0; y < (ssize_t) image->rows; y++)
194       {
195         stream=ReadBlobStream(image,length,pixels,&count);
196         if (count != (ssize_t) length)
197           break;
198       }
199     }
200   count=0;
201   length=0;
202   scene=0;
203   status=MagickTrue;
204   stream=NULL;
205   do
206   {
207     /*
208       Read pixels to virtual canvas image then push to image.
209     */
210     if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
211       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
212         break;
213     status=SetImageExtent(image,image->columns,image->rows,exception);
214     if (status == MagickFalse)
215       break;
216     if (SetImageColorspace(image,YCbCrColorspace,exception) == MagickFalse)
217       break;
218     switch (image_info->interlace)
219     {
220       case NoInterlace:
221       default:
222       {
223         /*
224           No interlacing:  YCbCrYCbCrYCbCrYCbCrYCbCrYCbCr...
225         */
226         if (scene == 0)
227           {
228             length=GetQuantumExtent(canvas_image,quantum_info,quantum_type);
229             stream=ReadBlobStream(image,length,pixels,&count);
230           }
231         for (y=0; y < (ssize_t) image->extract_info.height; y++)
232         {
233           const Quantum
234             *magick_restrict p;
235 
236           Quantum
237             *magick_restrict q;
238 
239           ssize_t
240             x;
241 
242           if (count != (ssize_t) length)
243             {
244               status=MagickFalse;
245               ThrowFileException(exception,CorruptImageError,
246                 "UnexpectedEndOfFile",image->filename);
247               break;
248             }
249           q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
250             exception);
251           if (q == (Quantum *) NULL)
252             break;
253           length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
254             quantum_info,quantum_type,(unsigned char *) stream,exception);
255           if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
256             break;
257           if (((y-image->extract_info.y) >= 0) &&
258               ((y-image->extract_info.y) < (ssize_t) image->rows))
259             {
260               p=GetVirtualPixels(canvas_image,canvas_image->extract_info.x,0,
261                 canvas_image->columns,1,exception);
262               q=QueueAuthenticPixels(image,0,y-image->extract_info.y,
263                 image->columns,1,exception);
264               if ((p == (const Quantum *) NULL) ||
265                   (q == (Quantum *) NULL))
266                 break;
267               for (x=0; x < (ssize_t) image->columns; x++)
268               {
269                 SetPixelRed(image,GetPixelRed(canvas_image,p),q);
270                 SetPixelGreen(image,GetPixelGreen(canvas_image,p),q);
271                 SetPixelBlue(image,GetPixelBlue(canvas_image,p),q);
272                 if (image->alpha_trait != UndefinedPixelTrait)
273                   SetPixelAlpha(image,GetPixelAlpha(canvas_image,p),q);
274                 p+=GetPixelChannels(canvas_image);
275                 q+=GetPixelChannels(image);
276               }
277               if (SyncAuthenticPixels(image,exception) == MagickFalse)
278                 break;
279             }
280           if (image->previous == (Image *) NULL)
281             {
282               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
283                 image->rows);
284               if (status == MagickFalse)
285                 break;
286             }
287           stream=ReadBlobStream(image,length,pixels,&count);
288         }
289         break;
290       }
291       case LineInterlace:
292       {
293         static QuantumType
294           quantum_types[4] =
295           {
296             RedQuantum,
297             GreenQuantum,
298             BlueQuantum,
299             OpacityQuantum
300           };
301 
302         /*
303           Line interlacing:  YYY...CbCbCb...CrCrCr...YYY...CbCbCb...CrCrCr...
304         */
305         if (scene == 0)
306           {
307             length=GetQuantumExtent(canvas_image,quantum_info,RedQuantum);
308             stream=ReadBlobStream(image,length,pixels,&count);
309           }
310         for (y=0; y < (ssize_t) image->extract_info.height; y++)
311         {
312           for (i=0; i < (ssize_t) (image->alpha_trait != UndefinedPixelTrait ? 4 : 3); i++)
313           {
314             const Quantum
315               *magick_restrict p;
316 
317             Quantum
318               *magick_restrict q;
319 
320             ssize_t
321               x;
322 
323             if (count != (ssize_t) length)
324               {
325                 status=MagickFalse;
326                 ThrowFileException(exception,CorruptImageError,
327                   "UnexpectedEndOfFile",image->filename);
328                 break;
329               }
330             quantum_type=quantum_types[i];
331             q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
332               exception);
333             if (q == (Quantum *) NULL)
334               break;
335             length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
336               quantum_info,quantum_type,(unsigned char *) stream,exception);
337             if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
338               break;
339             if (((y-image->extract_info.y) >= 0) &&
340                 ((y-image->extract_info.y) < (ssize_t) image->rows))
341               {
342                 p=GetVirtualPixels(canvas_image,canvas_image->extract_info.x,
343                   0,canvas_image->columns,1,exception);
344                 q=GetAuthenticPixels(image,0,y-image->extract_info.y,
345                   image->columns,1,exception);
346                 if ((p == (const Quantum *) NULL) ||
347                     (q == (Quantum *) NULL))
348                   break;
349                 for (x=0; x < (ssize_t) image->columns; x++)
350                 {
351                   switch (quantum_type)
352                   {
353                     case RedQuantum:
354                     {
355                       SetPixelRed(image,GetPixelRed(canvas_image,p),q);
356                       break;
357                     }
358                     case GreenQuantum:
359                     {
360                       SetPixelGreen(image,GetPixelGreen(canvas_image,p),q);
361                       break;
362                     }
363                     case BlueQuantum:
364                     {
365                       SetPixelBlue(image,GetPixelBlue(canvas_image,p),q);
366                       break;
367                     }
368                     case OpacityQuantum:
369                     {
370                       SetPixelAlpha(image,GetPixelAlpha(canvas_image,p),q);
371                       break;
372                     }
373                     default:
374                       break;
375                   }
376                   p+=GetPixelChannels(canvas_image);
377                   q+=GetPixelChannels(image);
378                 }
379                 if (SyncAuthenticPixels(image,exception) == MagickFalse)
380                   break;
381               }
382             stream=ReadBlobStream(image,length,pixels,&count);
383           }
384           if (image->previous == (Image *) NULL)
385             {
386               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
387                 image->rows);
388               if (status == MagickFalse)
389                 break;
390             }
391         }
392         break;
393       }
394       case PlaneInterlace:
395       {
396         /*
397           Plane interlacing:  YYYYYY...CbCbCbCbCbCb...CrCrCrCrCrCr...
398         */
399         if (scene == 0)
400           {
401             length=GetQuantumExtent(canvas_image,quantum_info,RedQuantum);
402             stream=ReadBlobStream(image,length,pixels,&count);
403           }
404         for (y=0; y < (ssize_t) image->extract_info.height; y++)
405         {
406           const Quantum
407             *magick_restrict p;
408 
409           Quantum
410             *magick_restrict q;
411 
412           ssize_t
413             x;
414 
415           if (count != (ssize_t) length)
416             {
417               status=MagickFalse;
418               ThrowFileException(exception,CorruptImageError,
419                 "UnexpectedEndOfFile",image->filename);
420               break;
421             }
422           q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
423             exception);
424           if (q == (Quantum *) NULL)
425             break;
426           length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
427             quantum_info,RedQuantum,(unsigned char *) stream,exception);
428           if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
429             break;
430           if (((y-image->extract_info.y) >= 0) &&
431               ((y-image->extract_info.y) < (ssize_t) image->rows))
432             {
433               p=GetVirtualPixels(canvas_image,canvas_image->extract_info.x,0,
434                 canvas_image->columns,1,exception);
435               q=GetAuthenticPixels(image,0,y-image->extract_info.y,
436                 image->columns,1,exception);
437               if ((p == (const Quantum *) NULL) ||
438                   (q == (Quantum *) NULL))
439                 break;
440               for (x=0; x < (ssize_t) image->columns; x++)
441               {
442                 SetPixelRed(image,GetPixelRed(canvas_image,p),q);
443                 p+=GetPixelChannels(canvas_image);
444                 q+=GetPixelChannels(image);
445               }
446               if (SyncAuthenticPixels(image,exception) == MagickFalse)
447                 break;
448             }
449           stream=ReadBlobStream(image,length,pixels,&count);
450         }
451         if (image->previous == (Image *) NULL)
452           {
453             status=SetImageProgress(image,LoadImageTag,1,5);
454             if (status == MagickFalse)
455               break;
456           }
457         for (y=0; y < (ssize_t) image->extract_info.height; y++)
458         {
459           const Quantum
460             *magick_restrict p;
461 
462           Quantum
463             *magick_restrict q;
464 
465           ssize_t
466             x;
467 
468           if (count != (ssize_t) length)
469             {
470               status=MagickFalse;
471               ThrowFileException(exception,CorruptImageError,
472                 "UnexpectedEndOfFile",image->filename);
473               break;
474             }
475           q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
476             exception);
477           if (q == (Quantum *) NULL)
478             break;
479           length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
480             quantum_info,GreenQuantum,(unsigned char *) stream,exception);
481           if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
482             break;
483           if (((y-image->extract_info.y) >= 0) &&
484               ((y-image->extract_info.y) < (ssize_t) image->rows))
485             {
486               p=GetVirtualPixels(canvas_image,canvas_image->extract_info.x,0,
487                 canvas_image->columns,1,exception);
488               q=GetAuthenticPixels(image,0,y-image->extract_info.y,
489                 image->columns,1,exception);
490               if ((p == (const Quantum *) NULL) ||
491                   (q == (Quantum *) NULL))
492                 break;
493               for (x=0; x < (ssize_t) image->columns; x++)
494               {
495                 SetPixelGreen(image,GetPixelGreen(canvas_image,p),q);
496                 p+=GetPixelChannels(canvas_image);
497                 q+=GetPixelChannels(image);
498               }
499               if (SyncAuthenticPixels(image,exception) == MagickFalse)
500                 break;
501             }
502           stream=ReadBlobStream(image,length,pixels,&count);
503         }
504         if (image->previous == (Image *) NULL)
505           {
506             status=SetImageProgress(image,LoadImageTag,2,5);
507             if (status == MagickFalse)
508               break;
509           }
510         for (y=0; y < (ssize_t) image->extract_info.height; y++)
511         {
512           const Quantum
513             *magick_restrict p;
514 
515           Quantum
516             *magick_restrict q;
517 
518           ssize_t
519             x;
520 
521           if (count != (ssize_t) length)
522             {
523               status=MagickFalse;
524               ThrowFileException(exception,CorruptImageError,
525                 "UnexpectedEndOfFile",image->filename);
526               break;
527             }
528           q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
529             exception);
530           if (q == (Quantum *) NULL)
531             break;
532           length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
533             quantum_info,BlueQuantum,(unsigned char *) stream,exception);
534           if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
535             break;
536           if (((y-image->extract_info.y) >= 0) &&
537               ((y-image->extract_info.y) < (ssize_t) image->rows))
538             {
539               p=GetVirtualPixels(canvas_image,canvas_image->extract_info.x,0,
540                 canvas_image->columns,1,exception);
541               q=GetAuthenticPixels(image,0,y-image->extract_info.y,
542                 image->columns,1,exception);
543               if ((p == (const Quantum *) NULL) ||
544                   (q == (Quantum *) NULL))
545                 break;
546               for (x=0; x < (ssize_t) image->columns; x++)
547               {
548                 SetPixelBlue(image,GetPixelBlue(canvas_image,p),q);
549                 p+=GetPixelChannels(canvas_image);
550                 q+=GetPixelChannels(image);
551               }
552               if (SyncAuthenticPixels(image,exception) == MagickFalse)
553                 break;
554             }
555           stream=ReadBlobStream(image,length,pixels,&count);
556         }
557         if (image->previous == (Image *) NULL)
558           {
559             status=SetImageProgress(image,LoadImageTag,3,5);
560             if (status == MagickFalse)
561               break;
562           }
563         if (image->alpha_trait != UndefinedPixelTrait)
564           {
565             for (y=0; y < (ssize_t) image->extract_info.height; y++)
566             {
567               const Quantum
568                 *magick_restrict p;
569 
570               Quantum
571                 *magick_restrict q;
572 
573               ssize_t
574                 x;
575 
576               if (count != (ssize_t) length)
577                 {
578                   status=MagickFalse;
579                   ThrowFileException(exception,CorruptImageError,
580                     "UnexpectedEndOfFile",image->filename);
581                   break;
582                 }
583               q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
584                 exception);
585               if (q == (Quantum *) NULL)
586                 break;
587               length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
588                 quantum_info,AlphaQuantum,(unsigned char *) stream,exception);
589               if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
590                 break;
591               if (((y-image->extract_info.y) >= 0) &&
592                   ((y-image->extract_info.y) < (ssize_t) image->rows))
593                 {
594                   p=GetVirtualPixels(canvas_image,
595                     canvas_image->extract_info.x,0,canvas_image->columns,1,
596                     exception);
597                   q=GetAuthenticPixels(image,0,y-image->extract_info.y,
598                     image->columns,1,exception);
599                   if ((p == (const Quantum *) NULL) ||
600                       (q == (Quantum *) NULL))
601                     break;
602                   for (x=0; x < (ssize_t) image->columns; x++)
603                   {
604                     SetPixelAlpha(image,GetPixelAlpha(canvas_image,p),q);
605                     p+=GetPixelChannels(canvas_image);
606                     q+=GetPixelChannels(image);
607                   }
608                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
609                     break;
610                 }
611               stream=ReadBlobStream(image,length,pixels,&count);
612             }
613             if (image->previous == (Image *) NULL)
614               {
615                 status=SetImageProgress(image,LoadImageTag,4,5);
616                 if (status == MagickFalse)
617                   break;
618               }
619           }
620         if (image->previous == (Image *) NULL)
621           {
622             status=SetImageProgress(image,LoadImageTag,5,5);
623             if (status == MagickFalse)
624               break;
625           }
626         break;
627       }
628       case PartitionInterlace:
629       {
630         /*
631           Partition interlacing:  YYYYYY..., CbCbCbCbCbCb..., CrCrCrCrCrCr...
632         */
633         AppendImageFormat("Y",image->filename);
634         status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
635         if (status == MagickFalse)
636           break;
637         if (DiscardBlobBytes(image,(MagickSizeType) image->offset) == MagickFalse)
638           {
639             status=MagickFalse;
640             ThrowFileException(exception,CorruptImageError,
641               "UnexpectedEndOfFile",image->filename);
642             break;
643           }
644         length=GetQuantumExtent(canvas_image,quantum_info,RedQuantum);
645         for (i=0; i < (ssize_t) scene; i++)
646         {
647           for (y=0; y < (ssize_t) image->extract_info.height; y++)
648           {
649             stream=ReadBlobStream(image,length,pixels,&count);
650             if (count != (ssize_t) length)
651               break;
652           }
653           if (count != (ssize_t) length)
654             break;
655         }
656         stream=ReadBlobStream(image,length,pixels,&count);
657         for (y=0; y < (ssize_t) image->extract_info.height; y++)
658         {
659           const Quantum
660             *magick_restrict p;
661 
662           Quantum
663             *magick_restrict q;
664 
665           ssize_t
666             x;
667 
668           if (count != (ssize_t) length)
669             {
670               status=MagickFalse;
671               ThrowFileException(exception,CorruptImageError,
672                 "UnexpectedEndOfFile",image->filename);
673               break;
674             }
675           q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
676             exception);
677           if (q == (Quantum *) NULL)
678             break;
679           length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
680             quantum_info,RedQuantum,(unsigned char *) stream,exception);
681           if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
682             break;
683           if (((y-image->extract_info.y) >= 0) &&
684               ((y-image->extract_info.y) < (ssize_t) image->rows))
685             {
686               p=GetVirtualPixels(canvas_image,canvas_image->extract_info.x,0,
687                 canvas_image->columns,1,exception);
688               q=GetAuthenticPixels(image,0,y-image->extract_info.y,
689                 image->columns,1,exception);
690               if ((p == (const Quantum *) NULL) ||
691                   (q == (Quantum *) NULL))
692                 break;
693               for (x=0; x < (ssize_t) image->columns; x++)
694               {
695                 SetPixelRed(image,GetPixelRed(canvas_image,p),q);
696                 p+=GetPixelChannels(canvas_image);
697                 q+=GetPixelChannels(image);
698               }
699               if (SyncAuthenticPixels(image,exception) == MagickFalse)
700                 break;
701             }
702           stream=ReadBlobStream(image,length,pixels,&count);
703         }
704         if (image->previous == (Image *) NULL)
705           {
706             status=SetImageProgress(image,LoadImageTag,1,5);
707             if (status == MagickFalse)
708               break;
709           }
710         (void) CloseBlob(image);
711         AppendImageFormat("Cb",image->filename);
712         status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
713         if (status == MagickFalse)
714           break;
715         length=GetQuantumExtent(canvas_image,quantum_info,GreenQuantum);
716         for (i=0; i < (ssize_t) scene; i++)
717         {
718           for (y=0; y < (ssize_t) image->extract_info.height; y++)
719           {
720             stream=ReadBlobStream(image,length,pixels,&count);
721             if (count != (ssize_t) length)
722               break;
723           }
724           if (count != (ssize_t) length)
725             break;
726         }
727         stream=ReadBlobStream(image,length,pixels,&count);
728         for (y=0; y < (ssize_t) image->extract_info.height; y++)
729         {
730           const Quantum
731             *magick_restrict p;
732 
733           Quantum
734             *magick_restrict q;
735 
736           ssize_t
737             x;
738 
739           if (count != (ssize_t) length)
740             {
741               status=MagickFalse;
742               ThrowFileException(exception,CorruptImageError,
743                 "UnexpectedEndOfFile",image->filename);
744               break;
745             }
746           q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
747             exception);
748           if (q == (Quantum *) NULL)
749             break;
750           length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
751             quantum_info,GreenQuantum,(unsigned char *) stream,exception);
752           if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
753             break;
754           if (((y-image->extract_info.y) >= 0) &&
755               ((y-image->extract_info.y) < (ssize_t) image->rows))
756             {
757               p=GetVirtualPixels(canvas_image,canvas_image->extract_info.x,0,
758                 canvas_image->columns,1,exception);
759               q=GetAuthenticPixels(image,0,y-image->extract_info.y,
760                 image->columns,1,exception);
761               if ((p == (const Quantum *) NULL) ||
762                   (q == (Quantum *) NULL))
763                 break;
764               for (x=0; x < (ssize_t) image->columns; x++)
765               {
766                 SetPixelGreen(image,GetPixelGreen(canvas_image,p),q);
767                 p+=GetPixelChannels(canvas_image);
768                 q+=GetPixelChannels(image);
769               }
770               if (SyncAuthenticPixels(image,exception) == MagickFalse)
771                 break;
772            }
773           stream=ReadBlobStream(image,length,pixels,&count);
774         }
775         if (image->previous == (Image *) NULL)
776           {
777             status=SetImageProgress(image,LoadImageTag,2,5);
778             if (status == MagickFalse)
779               break;
780           }
781         (void) CloseBlob(image);
782         AppendImageFormat("Cr",image->filename);
783         status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
784         if (status == MagickFalse)
785           break;
786         length=GetQuantumExtent(canvas_image,quantum_info,BlueQuantum);
787         for (i=0; i < (ssize_t) scene; i++)
788         {
789           for (y=0; y < (ssize_t) image->extract_info.height; y++)
790           {
791             stream=ReadBlobStream(image,length,pixels,&count);
792             if (count != (ssize_t) length)
793               break;
794           }
795           if (count != (ssize_t) length)
796             break;
797         }
798         stream=ReadBlobStream(image,length,pixels,&count);
799         for (y=0; y < (ssize_t) image->extract_info.height; y++)
800         {
801           const Quantum
802             *magick_restrict p;
803 
804           Quantum
805             *magick_restrict q;
806 
807           ssize_t
808             x;
809 
810           if (count != (ssize_t) length)
811             {
812               status=MagickFalse;
813               ThrowFileException(exception,CorruptImageError,
814                 "UnexpectedEndOfFile",image->filename);
815               break;
816             }
817           q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
818             exception);
819           if (q == (Quantum *) NULL)
820             break;
821           length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
822             quantum_info,BlueQuantum,(unsigned char *) stream,exception);
823           if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
824             break;
825           if (((y-image->extract_info.y) >= 0) &&
826               ((y-image->extract_info.y) < (ssize_t) image->rows))
827             {
828               p=GetVirtualPixels(canvas_image,canvas_image->extract_info.x,0,
829                 canvas_image->columns,1,exception);
830               q=GetAuthenticPixels(image,0,y-image->extract_info.y,
831                 image->columns,1,exception);
832               if ((p == (const Quantum *) NULL) ||
833                   (q == (Quantum *) NULL))
834                 break;
835               for (x=0; x < (ssize_t) image->columns; x++)
836               {
837                 SetPixelBlue(image,GetPixelBlue(canvas_image,p),q);
838                 p+=GetPixelChannels(canvas_image);
839                 q+=GetPixelChannels(image);
840               }
841               if (SyncAuthenticPixels(image,exception) == MagickFalse)
842                 break;
843            }
844           stream=ReadBlobStream(image,length,pixels,&count);
845         }
846         if (image->previous == (Image *) NULL)
847           {
848             status=SetImageProgress(image,LoadImageTag,3,5);
849             if (status == MagickFalse)
850               break;
851           }
852         if (image->alpha_trait != UndefinedPixelTrait)
853           {
854             (void) CloseBlob(image);
855             AppendImageFormat("A",image->filename);
856             status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
857             if (status == MagickFalse)
858               break;
859             length=GetQuantumExtent(canvas_image,quantum_info,AlphaQuantum);
860             for (i=0; i < (ssize_t) scene; i++)
861             {
862               for (y=0; y < (ssize_t) image->extract_info.height; y++)
863               {
864                 stream=ReadBlobStream(image,length,pixels,&count);
865                 if (count != (ssize_t) length)
866                   break;
867               }
868               if (count != (ssize_t) length)
869                 break;
870             }
871             stream=ReadBlobStream(image,length,pixels,&count);
872             for (y=0; y < (ssize_t) image->extract_info.height; y++)
873             {
874               const Quantum
875                 *magick_restrict p;
876 
877               Quantum
878                 *magick_restrict q;
879 
880               ssize_t
881                 x;
882 
883               if (count != (ssize_t) length)
884                 {
885                   status=MagickFalse;
886                   ThrowFileException(exception,CorruptImageError,
887                     "UnexpectedEndOfFile",image->filename);
888                   break;
889                 }
890               q=GetAuthenticPixels(canvas_image,0,0,canvas_image->columns,1,
891                 exception);
892               if (q == (Quantum *) NULL)
893                 break;
894               length=ImportQuantumPixels(canvas_image,(CacheView *) NULL,
895                 quantum_info,BlueQuantum,(unsigned char *) stream,exception);
896               if (SyncAuthenticPixels(canvas_image,exception) == MagickFalse)
897                 break;
898               if (((y-image->extract_info.y) >= 0) &&
899                   ((y-image->extract_info.y) < (ssize_t) image->rows))
900                 {
901                   p=GetVirtualPixels(canvas_image,
902                     canvas_image->extract_info.x,0,canvas_image->columns,1,
903                     exception);
904                   q=GetAuthenticPixels(image,0,y-image->extract_info.y,
905                     image->columns,1,exception);
906                   if ((p == (const Quantum *) NULL) ||
907                       (q == (Quantum *) NULL))
908                     break;
909                   for (x=0; x < (ssize_t) image->columns; x++)
910                   {
911                     SetPixelAlpha(image,GetPixelAlpha(canvas_image,p),q);
912                     p+=GetPixelChannels(canvas_image);
913                     q+=GetPixelChannels(image);
914                   }
915                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
916                     break;
917                }
918               stream=ReadBlobStream(image,length,pixels,&count);
919             }
920             if (image->previous == (Image *) NULL)
921               {
922                 status=SetImageProgress(image,LoadImageTag,4,5);
923                 if (status == MagickFalse)
924                   break;
925               }
926           }
927         if (image->previous == (Image *) NULL)
928           {
929             status=SetImageProgress(image,LoadImageTag,5,5);
930             if (status == MagickFalse)
931               break;
932           }
933         break;
934       }
935     }
936     if (status == MagickFalse)
937       break;
938     SetQuantumImageType(image,quantum_type);
939     /*
940       Proceed to next image.
941     */
942     if (image_info->number_scenes != 0)
943       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
944         break;
945     if (count == (ssize_t) length)
946       {
947         /*
948           Allocate next image structure.
949         */
950         AcquireNextImage(image_info,image,exception);
951         if (GetNextImageInList(image) == (Image *) NULL)
952           {
953             status=MagickFalse;
954             break;
955           }
956         image=SyncNextImageInList(image);
957         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
958           GetBlobSize(image));
959         if (status == MagickFalse)
960           break;
961       }
962     scene++;
963   } while (count == (ssize_t) length);
964   quantum_info=DestroyQuantumInfo(quantum_info);
965   canvas_image=DestroyImage(canvas_image);
966   (void) CloseBlob(image);
967   if (status == MagickFalse)
968     return(DestroyImageList(image));
969   return(GetFirstImageInList(image));
970 }
971 
972 /*
973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
974 %                                                                             %
975 %                                                                             %
976 %                                                                             %
977 %   R e g i s t e r Y C b C r I m a g e                                       %
978 %                                                                             %
979 %                                                                             %
980 %                                                                             %
981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
982 %
983 %  RegisterYCBCRImage() adds attributes for the YCbCr or YCbCrA image format to
984 %  the list of supported formats.  The attributes include the image format
985 %  tag, a method to read and/or write the format, whether the format
986 %  supports the saving of more than one frame to the same file or blob,
987 %  whether the format supports native in-memory I/O, and a brief
988 %  description of the format.
989 %
990 %  The format of the RegisterYCBCRImage method is:
991 %
992 %      size_t RegisterYCBCRImage(void)
993 %
994 */
RegisterYCBCRImage(void)995 ModuleExport size_t RegisterYCBCRImage(void)
996 {
997   MagickInfo
998     *entry;
999 
1000   entry=AcquireMagickInfo("YCbCr","YCbCr","Raw Y, Cb, and Cr samples");
1001   entry->decoder=(DecodeImageHandler *) ReadYCBCRImage;
1002   entry->encoder=(EncodeImageHandler *) WriteYCBCRImage;
1003   entry->flags|=CoderRawSupportFlag;
1004   entry->flags|=CoderEndianSupportFlag;
1005   (void) RegisterMagickInfo(entry);
1006   entry=AcquireMagickInfo("YCbCr","YCbCrA","Raw Y, Cb, Cr, and alpha samples");
1007   entry->decoder=(DecodeImageHandler *) ReadYCBCRImage;
1008   entry->encoder=(EncodeImageHandler *) WriteYCBCRImage;
1009   entry->flags|=CoderRawSupportFlag;
1010   entry->flags|=CoderEndianSupportFlag;
1011   (void) RegisterMagickInfo(entry);
1012   return(MagickImageCoderSignature);
1013 }
1014 
1015 /*
1016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1017 %                                                                             %
1018 %                                                                             %
1019 %                                                                             %
1020 %   U n r e g i s t e r Y C b C r I m a g e                                   %
1021 %                                                                             %
1022 %                                                                             %
1023 %                                                                             %
1024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1025 %
1026 %  UnregisterYCBCRImage() removes format registrations made by the
1027 %  YCbCr module from the list of supported formats.
1028 %
1029 %  The format of the UnregisterYCBCRImage method is:
1030 %
1031 %      UnregisterYCBCRImage(void)
1032 %
1033 */
UnregisterYCBCRImage(void)1034 ModuleExport void UnregisterYCBCRImage(void)
1035 {
1036   (void) UnregisterMagickInfo("YCbCr");
1037   (void) UnregisterMagickInfo("YCbCrA");
1038 }
1039 
1040 /*
1041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1042 %                                                                             %
1043 %                                                                             %
1044 %                                                                             %
1045 %   W r i t e Y C b C r I m a g e                                             %
1046 %                                                                             %
1047 %                                                                             %
1048 %                                                                             %
1049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1050 %
1051 %  WriteYCBCRImage() writes an image to a file in the YCbCr or YCbCrA
1052 %  rasterfile format.
1053 %
1054 %  The format of the WriteYCBCRImage method is:
1055 %
1056 %      MagickBooleanType WriteYCBCRImage(const ImageInfo *image_info,
1057 %        Image *image,ExceptionInfo *exception)
1058 %
1059 %  A description of each parameter follows.
1060 %
1061 %    o image_info: the image info.
1062 %
1063 %    o image:  The image.
1064 %
1065 %    o exception: return any errors or warnings in this structure.
1066 %
1067 */
WriteYCBCRImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)1068 static MagickBooleanType WriteYCBCRImage(const ImageInfo *image_info,
1069   Image *image,ExceptionInfo *exception)
1070 {
1071   MagickBooleanType
1072     status;
1073 
1074   MagickOffsetType
1075     scene;
1076 
1077   QuantumInfo
1078     *quantum_info;
1079 
1080   QuantumType
1081     quantum_type;
1082 
1083   const Quantum
1084     *p;
1085 
1086   size_t
1087     imageListLength,
1088     length;
1089 
1090   ssize_t
1091     count,
1092     y;
1093 
1094   unsigned char
1095     *pixels;
1096 
1097   /*
1098     Allocate memory for pixels.
1099   */
1100   assert(image_info != (const ImageInfo *) NULL);
1101   assert(image_info->signature == MagickCoreSignature);
1102   assert(image != (Image *) NULL);
1103   assert(image->signature == MagickCoreSignature);
1104   if (image->debug != MagickFalse)
1105     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1106   if (image_info->interlace != PartitionInterlace)
1107     {
1108       /*
1109         Open output image file.
1110       */
1111       assert(exception != (ExceptionInfo *) NULL);
1112       assert(exception->signature == MagickCoreSignature);
1113       status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1114       if (status == MagickFalse)
1115         return(status);
1116     }
1117   quantum_type=RGBQuantum;
1118   if (LocaleCompare(image_info->magick,"YCbCrA") == 0)
1119     {
1120       quantum_type=RGBAQuantum;
1121       image->alpha_trait=BlendPixelTrait;
1122     }
1123   scene=0;
1124   imageListLength=GetImageListLength(image);
1125   do
1126   {
1127     /*
1128       Convert MIFF to YCbCr raster pixels.
1129     */
1130     if (image->colorspace != YCbCrColorspace)
1131       (void) TransformImageColorspace(image,YCbCrColorspace,exception);
1132     if ((LocaleCompare(image_info->magick,"YCbCrA") == 0) &&
1133         (image->alpha_trait == UndefinedPixelTrait))
1134       (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1135     quantum_info=AcquireQuantumInfo(image_info,image);
1136     if (quantum_info == (QuantumInfo *) NULL)
1137       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1138     pixels=(unsigned char *) GetQuantumPixels(quantum_info);
1139     switch (image_info->interlace)
1140     {
1141       case NoInterlace:
1142       default:
1143       {
1144         /*
1145           No interlacing:  YCbCrYCbCrYCbCrYCbCrYCbCrYCbCr...
1146         */
1147         for (y=0; y < (ssize_t) image->rows; y++)
1148         {
1149           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1150           if (p == (const Quantum *) NULL)
1151             break;
1152           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1153             quantum_type,pixels,exception);
1154           count=WriteBlob(image,length,pixels);
1155           if (count != (ssize_t) length)
1156             break;
1157           if (image->previous == (Image *) NULL)
1158             {
1159               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1160                 image->rows);
1161               if (status == MagickFalse)
1162                 break;
1163             }
1164         }
1165         break;
1166       }
1167       case LineInterlace:
1168       {
1169         /*
1170           Line interlacing:  YYY...CbCbCb...CrCrCr...YYY...CbCbCb...CrCrCr...
1171         */
1172         for (y=0; y < (ssize_t) image->rows; y++)
1173         {
1174           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1175           if (p == (const Quantum *) NULL)
1176             break;
1177           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1178             RedQuantum,pixels,exception);
1179           count=WriteBlob(image,length,pixels);
1180           if (count != (ssize_t) length)
1181             break;
1182           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1183             GreenQuantum,pixels,exception);
1184           count=WriteBlob(image,length,pixels);
1185           if (count != (ssize_t) length)
1186             break;
1187           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1188             BlueQuantum,pixels,exception);
1189           count=WriteBlob(image,length,pixels);
1190           if (count != (ssize_t) length)
1191             break;
1192           if (quantum_type == RGBAQuantum)
1193             {
1194               length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1195                 AlphaQuantum,pixels,exception);
1196               count=WriteBlob(image,length,pixels);
1197               if (count != (ssize_t) length)
1198                 break;
1199             }
1200           if (image->previous == (Image *) NULL)
1201             {
1202               status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1203                 image->rows);
1204               if (status == MagickFalse)
1205                 break;
1206             }
1207         }
1208         break;
1209       }
1210       case PlaneInterlace:
1211       {
1212         /*
1213           Plane interlacing:  YYYYYY...CbCbCbCbCbCb...CrCrCrCrCrCr...
1214         */
1215         for (y=0; y < (ssize_t) image->rows; y++)
1216         {
1217           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1218           if (p == (const Quantum *) NULL)
1219             break;
1220           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1221             RedQuantum,pixels,exception);
1222           count=WriteBlob(image,length,pixels);
1223           if (count != (ssize_t) length)
1224             break;
1225         }
1226         if (image->previous == (Image *) NULL)
1227           {
1228             status=SetImageProgress(image,SaveImageTag,1,5);
1229             if (status == MagickFalse)
1230               break;
1231           }
1232         for (y=0; y < (ssize_t) image->rows; y++)
1233         {
1234           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1235           if (p == (const Quantum *) NULL)
1236             break;
1237           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1238             GreenQuantum,pixels,exception);
1239           count=WriteBlob(image,length,pixels);
1240           if (count != (ssize_t) length)
1241             break;
1242         }
1243         if (image->previous == (Image *) NULL)
1244           {
1245             status=SetImageProgress(image,SaveImageTag,2,5);
1246             if (status == MagickFalse)
1247               break;
1248           }
1249         for (y=0; y < (ssize_t) image->rows; y++)
1250         {
1251           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1252           if (p == (const Quantum *) NULL)
1253             break;
1254           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1255             BlueQuantum,pixels,exception);
1256           count=WriteBlob(image,length,pixels);
1257           if (count != (ssize_t) length)
1258             break;
1259         }
1260         if (image->previous == (Image *) NULL)
1261           {
1262             status=SetImageProgress(image,SaveImageTag,3,5);
1263             if (status == MagickFalse)
1264               break;
1265           }
1266         if (quantum_type == RGBAQuantum)
1267           {
1268             for (y=0; y < (ssize_t) image->rows; y++)
1269             {
1270               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1271               if (p == (const Quantum *) NULL)
1272                 break;
1273               length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1274                 AlphaQuantum,pixels,exception);
1275               count=WriteBlob(image,length,pixels);
1276               if (count != (ssize_t) length)
1277               break;
1278             }
1279           }
1280         if (image_info->interlace == PartitionInterlace)
1281           (void) CopyMagickString(image->filename,image_info->filename,
1282             MagickPathExtent);
1283         if (image->previous == (Image *) NULL)
1284           {
1285             status=SetImageProgress(image,SaveImageTag,5,5);
1286             if (status == MagickFalse)
1287               break;
1288           }
1289         break;
1290       }
1291       case PartitionInterlace:
1292       {
1293         /*
1294           Partition interlacing:  YYYYYY..., CbCbCbCbCbCb..., CrCrCrCrCrCr...
1295         */
1296         AppendImageFormat("Y",image->filename);
1297         status=OpenBlob(image_info,image,scene == 0 ? WriteBinaryBlobMode :
1298           AppendBinaryBlobMode,exception);
1299         if (status == MagickFalse)
1300           return(status);
1301         for (y=0; y < (ssize_t) image->rows; y++)
1302         {
1303           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1304           if (p == (const Quantum *) NULL)
1305             break;
1306           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1307             RedQuantum,pixels,exception);
1308           count=WriteBlob(image,length,pixels);
1309           if (count != (ssize_t) length)
1310             break;
1311         }
1312         if (image->previous == (Image *) NULL)
1313           {
1314             status=SetImageProgress(image,SaveImageTag,1,5);
1315             if (status == MagickFalse)
1316               break;
1317           }
1318         (void) CloseBlob(image);
1319         AppendImageFormat("Cb",image->filename);
1320         status=OpenBlob(image_info,image,scene == 0 ? WriteBinaryBlobMode :
1321           AppendBinaryBlobMode,exception);
1322         if (status == MagickFalse)
1323           return(status);
1324         for (y=0; y < (ssize_t) image->rows; y++)
1325         {
1326           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1327           if (p == (const Quantum *) NULL)
1328             break;
1329           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1330             GreenQuantum,pixels,exception);
1331           count=WriteBlob(image,length,pixels);
1332           if (count != (ssize_t) length)
1333             break;
1334         }
1335         if (image->previous == (Image *) NULL)
1336           {
1337             status=SetImageProgress(image,SaveImageTag,2,5);
1338             if (status == MagickFalse)
1339               break;
1340           }
1341         (void) CloseBlob(image);
1342         AppendImageFormat("Cr",image->filename);
1343         status=OpenBlob(image_info,image,scene == 0 ? WriteBinaryBlobMode :
1344           AppendBinaryBlobMode,exception);
1345         if (status == MagickFalse)
1346           return(status);
1347         for (y=0; y < (ssize_t) image->rows; y++)
1348         {
1349           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1350           if (p == (const Quantum *) NULL)
1351             break;
1352           length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1353             BlueQuantum,pixels,exception);
1354           count=WriteBlob(image,length,pixels);
1355           if (count != (ssize_t) length)
1356             break;
1357         }
1358         if (image->previous == (Image *) NULL)
1359           {
1360             status=SetImageProgress(image,SaveImageTag,3,5);
1361             if (status == MagickFalse)
1362               break;
1363           }
1364         if (quantum_type == RGBAQuantum)
1365           {
1366             (void) CloseBlob(image);
1367             AppendImageFormat("A",image->filename);
1368             status=OpenBlob(image_info,image,scene == 0 ? WriteBinaryBlobMode :
1369               AppendBinaryBlobMode,exception);
1370             if (status == MagickFalse)
1371               return(status);
1372             for (y=0; y < (ssize_t) image->rows; y++)
1373             {
1374               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1375               if (p == (const Quantum *) NULL)
1376                 break;
1377               length=ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
1378                 AlphaQuantum,pixels,exception);
1379               count=WriteBlob(image,length,pixels);
1380               if (count != (ssize_t) length)
1381                 break;
1382             }
1383             if (image->previous == (Image *) NULL)
1384               {
1385                 status=SetImageProgress(image,SaveImageTag,4,5);
1386                 if (status == MagickFalse)
1387                   break;
1388               }
1389           }
1390         (void) CloseBlob(image);
1391         (void) CopyMagickString(image->filename,image_info->filename,
1392           MagickPathExtent);
1393         if (image->previous == (Image *) NULL)
1394           {
1395             status=SetImageProgress(image,SaveImageTag,5,5);
1396             if (status == MagickFalse)
1397               break;
1398           }
1399         break;
1400       }
1401     }
1402     quantum_info=DestroyQuantumInfo(quantum_info);
1403     if (GetNextImageInList(image) == (Image *) NULL)
1404       break;
1405     image=SyncNextImageInList(image);
1406     status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
1407     if (status == MagickFalse)
1408       break;
1409   } while (image_info->adjoin != MagickFalse);
1410   (void) CloseBlob(image);
1411   return(MagickTrue);
1412 }
1413