• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                     IIIIIIIIII    PPPPPPPP      LL                          %
7 %                         II        PP      PP    LL                          %
8 %                         II        PP       PP   LL                          %
9 %                         II        PP      PP    LL                          %
10 %                         II        PPPPPPPP      LL                          %
11 %                         II        PP            LL                          %
12 %                         II        PP            LL                          %
13 %                     IIIIIIIIII    PP            LLLLLLLL                    %
14 %                                                                             %
15 %                                                                             %
16 %                                                                             %
17 %                   Read/Write Scanalytics IPLab Image Format                 %
18 %                                  Sean Burke                                 %
19 %                                  2008.05.07                                 %
20 %                                     v 0.9                                   %
21 %                                                                             %
22 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
23 %  dedicated to making software imaging solutions freely available.           %
24 %                                                                             %
25 %  You may not use this file except in compliance with the License.  You may  %
26 %  obtain a copy of the License at                                            %
27 %                                                                             %
28 %    http://www.imagemagick.org/script/license.php                            %
29 %                                                                             %
30 %  Unless required by applicable law or agreed to in writing, software        %
31 %  distributed under the License is distributed on an "AS IS" BASIS,          %
32 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
33 %  See the License for the specific language governing permissions and        %
34 %  limitations under the License.                                             %
35 %                                                                             %
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 %
38 %
39 */
40 
41 /*
42  Include declarations.
43  */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/blob-private.h"
47 #include "MagickCore/cache.h"
48 #include "MagickCore/colorspace.h"
49 #include "MagickCore/colorspace-private.h"
50 #include "MagickCore/exception.h"
51 #include "MagickCore/exception-private.h"
52 #include "MagickCore/image.h"
53 #include "MagickCore/image-private.h"
54 #include "MagickCore/list.h"
55 #include "MagickCore/magick.h"
56 #include "MagickCore/memory_.h"
57 #include "MagickCore/monitor.h"
58 #include "MagickCore/monitor-private.h"
59 #include "MagickCore/option.h"
60 #include "MagickCore/property.h"
61 #include "MagickCore/quantum-private.h"
62 #include "MagickCore/static.h"
63 #include "MagickCore/string_.h"
64 #include "MagickCore/module.h"
65 
66 /*
67 Tyedef declarations
68  */
69 
70 typedef struct _IPLInfo
71 {
72   unsigned int
73     tag,
74     size,
75     time,
76     z,
77     width,
78     height,
79     colors,
80     depth,
81     byteType;
82 } IPLInfo;
83 
84 static MagickBooleanType
85   WriteIPLImage(const ImageInfo *,Image *,ExceptionInfo *);
86 
87 /*
88 static void increase (void *pixel, int byteType){
89   switch(byteType){
90     case 0:(*((unsigned char *) pixel))++; break;
91     case 1:(*((signed int *) pixel))++; break;
92     case 2:(*((unsigned int *) pixel))++; break;
93     case 3:(*((signed long *) pixel))++; break;
94     default:(*((unsigned int *) pixel))++; break;
95   }
96 }
97 */
98 
99 /*
100  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101  %                                                                             %
102  %                                                                             %
103  %                                                                             %
104  %   I s I P L                                                                 %
105  %                                                                             %
106  %                                                                             %
107  %                                                                             %
108  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109  %
110  %  IsIPL() returns MagickTrue if the image format type, identified by the
111  %  magick string, is IPL.
112  %
113  %  The format of the IsIPL method is:
114  %
115  %      MagickBooleanType IsIPL(const unsigned char *magick,const size_t length)
116  %
117  %  A description of each parameter follows:
118  %
119  %    o magick: compare image format pattern against these bytes.
120  %
121  %    o length: Specifies the length of the magick string.
122  %
123  */
IsIPL(const unsigned char * magick,const size_t length)124 static MagickBooleanType IsIPL(const unsigned char *magick,const size_t length)
125 {
126   if (length < 4)
127     return(MagickFalse);
128   if (LocaleNCompare((const char *) magick,"data",4) == 0)
129     return(MagickTrue);
130   return(MagickFalse);
131 }
132 
133 /*
134  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
135  %                                                                             %
136  %                                                                             %
137  %                                                                             %
138  %    R e a d I P L I m a g e                                                  %
139  %                                                                             %
140  %                                                                             %
141  %                                                                             %
142  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143  %
144  %  ReadIPLImage() reads a Scanalytics IPLab image file and returns it.  It
145  %  allocates the memory necessary for the new Image structure and returns a
146  %  pointer to the new image.
147  %
148  %  According to the IPLab spec, the data is blocked out in five dimensions:
149  %  { t, z, c, y, x }.  When we return the image, the latter three are folded
150  %  into the standard "Image" structure.  The "scenes" (image_info->scene)
151  %  correspond to the order: { {t0,z0}, {t0, z1}, ..., {t1,z0}, {t1,z1}... }
152  %  The number of scenes is t*z.
153  %
154  %  The format of the ReadIPLImage method is:
155  %
156  %      Image *ReadIPLImage(const ImageInfo *image_info,ExceptionInfo *exception)
157  %
158  %  A description of each parameter follows:
159  %
160  %    o image_info: The image info.
161  %
162  %    o exception: return any errors or warnings in this structure.
163  %
164  */
165 
SetHeaderFromIPL(Image * image,IPLInfo * ipl)166 static void SetHeaderFromIPL(Image *image, IPLInfo *ipl){
167   image->columns = ipl->width;
168   image->rows = ipl->height;
169   image->depth = ipl->depth;
170   image->resolution.x = 1;
171   image->resolution.y = 1;
172 }
173 
174 
ReadIPLImage(const ImageInfo * image_info,ExceptionInfo * exception)175 static Image *ReadIPLImage(const ImageInfo *image_info,ExceptionInfo *exception)
176 {
177 
178   /*
179   Declare variables
180    */
181   Image *image;
182 
183   MagickBooleanType status;
184   register Quantum *q;
185   unsigned char magick[12], *pixels;
186   ssize_t count;
187   ssize_t y;
188   size_t t_count=0;
189   size_t length;
190   IPLInfo
191     ipl_info;
192   QuantumFormatType
193     quantum_format;
194   QuantumInfo
195     *quantum_info;
196   QuantumType
197     quantum_type;
198 
199   /*
200    Open Image
201    */
202 
203   assert(image_info != (const ImageInfo *) NULL);
204   assert(image_info->signature == MagickCoreSignature);
205   if ( image_info->debug != MagickFalse)
206     (void) LogMagickEvent(TraceEvent, GetMagickModule(), "%s",
207                 image_info->filename);
208   assert(exception != (ExceptionInfo *) NULL);
209   assert(exception->signature == MagickCoreSignature);
210   image=AcquireImage(image_info,exception);
211   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
212   if (status == MagickFalse)
213   {
214     image=DestroyImageList(image);
215     return((Image *) NULL);
216   }
217 
218   /*
219    Read IPL image
220    */
221 
222   /*
223     Determine endianness
224    If we get back "iiii", we have LSB,"mmmm", MSB
225    */
226   count=ReadBlob(image,4,magick);
227   (void) count;
228   if((LocaleNCompare((char *) magick,"iiii",4) == 0))
229     image->endian=LSBEndian;
230   else{
231     if((LocaleNCompare((char *) magick,"mmmm",4) == 0))
232       image->endian=MSBEndian;
233     else{
234       ThrowReaderException(CorruptImageError, "ImproperImageHeader");
235     }
236   }
237   /* Skip o'er the next 8 bytes (garbage) */
238   count=ReadBlob(image, 8, magick);
239   /*
240    Excellent, now we read the header unimpeded.
241    */
242   count=ReadBlob(image,4,magick);
243   if((LocaleNCompare((char *) magick,"data",4) != 0))
244     ThrowReaderException(CorruptImageError, "ImproperImageHeader");
245   ipl_info.size=ReadBlobLong(image);
246   ipl_info.width=ReadBlobLong(image);
247   ipl_info.height=ReadBlobLong(image);
248   if((ipl_info.width == 0UL) || (ipl_info.height == 0UL))
249     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
250   ipl_info.colors=ReadBlobLong(image);
251   if(ipl_info.colors == 3){ SetImageColorspace(image,sRGBColorspace,exception);}
252   else { image->colorspace = GRAYColorspace; }
253   ipl_info.z=ReadBlobLong(image);
254   ipl_info.time=ReadBlobLong(image);
255 
256   ipl_info.byteType=ReadBlobLong(image);
257 
258 
259   /* Initialize Quantum Info */
260 
261   switch (ipl_info.byteType) {
262     case 0:
263       ipl_info.depth=8;
264       quantum_format = UnsignedQuantumFormat;
265       break;
266     case 1:
267       ipl_info.depth=16;
268       quantum_format = SignedQuantumFormat;
269       break;
270     case 2:
271       ipl_info.depth=16;
272       quantum_format = UnsignedQuantumFormat;
273       break;
274     case 3:
275       ipl_info.depth=32;
276       quantum_format = SignedQuantumFormat;
277       break;
278     case 4: ipl_info.depth=32;
279       quantum_format = FloatingPointQuantumFormat;
280       break;
281     case 5:
282       ipl_info.depth=8;
283       quantum_format = UnsignedQuantumFormat;
284       break;
285     case 6:
286       ipl_info.depth=16;
287       quantum_format = UnsignedQuantumFormat;
288       break;
289     case 10:
290       ipl_info.depth=64;
291       quantum_format = FloatingPointQuantumFormat;
292       break;
293     default:
294       ipl_info.depth=16;
295       quantum_format = UnsignedQuantumFormat;
296       break;
297   }
298 
299   /*
300     Set number of scenes of image
301   */
302 
303   SetHeaderFromIPL(image, &ipl_info);
304 
305   /* Thats all we need if we are pinging. */
306   if (image_info->ping != MagickFalse)
307     {
308       (void) CloseBlob(image);
309       return(GetFirstImageInList(image));
310     }
311   length=image->columns;
312   quantum_type=GetQuantumType(image,exception);
313  do
314   {
315     SetHeaderFromIPL(image, &ipl_info);
316 
317     if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
318       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
319         break;
320     status=SetImageExtent(image,image->columns,image->rows,exception);
321     if (status == MagickFalse)
322       return(DestroyImageList(image));
323 /*
324    printf("Length: %.20g, Memory size: %.20g\n", (double) length,(double)
325      image->depth);
326 */
327      quantum_info=AcquireQuantumInfo(image_info,image);
328      if (quantum_info == (QuantumInfo *) NULL)
329        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
330      status=SetQuantumFormat(image,quantum_info,quantum_format);
331      if (status == MagickFalse)
332        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
333      pixels=(unsigned char *) GetQuantumPixels(quantum_info);
334      if(image->columns != ipl_info.width){
335 /*
336      printf("Columns not set correctly!  Wanted: %.20g, got: %.20g\n",
337        (double) ipl_info.width, (double) image->columns);
338 */
339      }
340 
341     /*
342     Covert IPL binary to pixel packets
343      */
344 
345   if(ipl_info.colors == 1){
346       for(y = 0; y < (ssize_t) image->rows; y++){
347         (void) ReadBlob(image, length*image->depth/8, pixels);
348         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
349         if (q == (Quantum *) NULL)
350                 break;
351         (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
352           GrayQuantum,pixels,exception);
353         if (SyncAuthenticPixels(image,exception) == MagickFalse)
354           break;
355     }
356   }
357   else{
358       for(y = 0; y < (ssize_t) image->rows; y++){
359         (void) ReadBlob(image, length*image->depth/8, pixels);
360         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
361         if (q == (Quantum *) NULL)
362                 break;
363         (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
364           RedQuantum,pixels,exception);
365         if (SyncAuthenticPixels(image,exception) == MagickFalse)
366           break;
367       }
368       for(y = 0; y < (ssize_t) image->rows; y++){
369         (void) ReadBlob(image, length*image->depth/8, pixels);
370         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
371         if (q == (Quantum *) NULL)
372           break;
373         (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
374           GreenQuantum,pixels,exception);
375         if (SyncAuthenticPixels(image,exception) == MagickFalse)
376           break;
377       }
378       for(y = 0; y < (ssize_t) image->rows; y++){
379         (void) ReadBlob(image, length*image->depth/8, pixels);
380         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
381         if (q == (Quantum *) NULL)
382           break;
383         (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
384           BlueQuantum,pixels,exception);
385         if (SyncAuthenticPixels(image,exception) == MagickFalse)
386           break;
387       }
388    }
389    SetQuantumImageType(image,quantum_type);
390 
391     t_count++;
392   quantum_info = DestroyQuantumInfo(quantum_info);
393 
394     if (EOFBlob(image) != MagickFalse)
395     {
396       ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
397                  image->filename);
398       break;
399     }
400    if(t_count < ipl_info.z * ipl_info.time){
401       /*
402        Proceed to next image.
403        */
404       AcquireNextImage(image_info,image,exception);
405       if (GetNextImageInList(image) == (Image *) NULL)
406       {
407         image=DestroyImageList(image);
408         return((Image *) NULL);
409       }
410       image=SyncNextImageInList(image);
411       status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
412         GetBlobSize(image));
413       if (status == MagickFalse)
414         break;
415     }
416   } while (t_count < ipl_info.z*ipl_info.time);
417   CloseBlob(image);
418   return(GetFirstImageInList(image));
419 }
420 
421 /*
422  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
423  %                                                                             %
424  %                                                                             %
425  %                                                                             %
426  %   R e g i s t e r I P L I m a g e                                           %
427  %                                                                             %
428  %                                                                             %
429  %                                                                             %
430  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
431  %
432  % RegisterIPLImage() add attributes for the Scanalytics IPL image format to the
433  % list of supported formats.
434  %
435  %
436  */
RegisterIPLImage(void)437 ModuleExport size_t RegisterIPLImage(void)
438 {
439   MagickInfo
440     *entry;
441 
442   entry=AcquireMagickInfo("IPL","IPL","IPL Image Sequence");
443   entry->decoder=(DecodeImageHandler *) ReadIPLImage;
444   entry->encoder=(EncodeImageHandler *) WriteIPLImage;
445   entry->magick=(IsImageFormatHandler *) IsIPL;
446   entry->flags|=CoderEndianSupportFlag;
447   (void) RegisterMagickInfo(entry);
448   return(MagickImageCoderSignature);
449 }
450 
451 /*
452  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
453  %                                                                             %
454  %                                                                             %
455  %                                                                             %
456  %   U n r e g i s t e r I P L I m a g e                                       %
457  %                                                                             %
458  %                                                                             %
459  %                                                                             %
460  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461  %
462  %  UnregisterIPLImage() removes format registrations made by the
463  %  IPL module from the list of supported formats.
464  %
465  %  The format of the UnregisterIPLImage method is:
466  %
467  %      UnregisterIPLImage(void)
468  %
469  */
UnregisterIPLImage(void)470 ModuleExport void UnregisterIPLImage(void)
471 {
472   (void) UnregisterMagickInfo("IPL");
473 }
474 
475 /*
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477 %                                                                             %
478 %                                                                             %
479 %                                                                             %
480 %   W r i t e I P L I m a g e                                                 %
481 %                                                                             %
482 %                                                                             %
483 %                                                                             %
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 %
486 %  WriteIPLImage() writes an image to a file in Scanalytics IPLabimage format.
487 %
488 %  The format of the WriteIPLImage method is:
489 %
490 %      MagickBooleanType WriteIPLImage(const ImageInfo *image_info,Image *image)
491 %       Image *image,ExceptionInfo *exception)
492 %
493 %  A description of each parameter follows.
494 %
495 %    o image_info: The image info.
496 %
497 %    o image:  The image.
498 %
499 %    o exception: return any errors or warnings in this structure.
500 %
501 */
WriteIPLImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)502 static MagickBooleanType WriteIPLImage(const ImageInfo *image_info,Image *image,
503   ExceptionInfo *exception)
504 {
505   IPLInfo
506     ipl_info;
507 
508   MagickBooleanType
509     status;
510 
511   MagickOffsetType
512     scene;
513 
514   register const Quantum
515     *p;
516 
517   QuantumInfo
518     *quantum_info;
519 
520   ssize_t
521     y;
522 
523   unsigned char
524     *pixels;
525 
526    /*
527     Open output image file.
528   */
529   assert(image_info != (const ImageInfo *) NULL);
530   assert(image_info->signature == MagickCoreSignature);
531   assert(image != (Image *) NULL);
532   assert(image->signature == MagickCoreSignature);
533   if (image->debug != MagickFalse)
534     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
535   assert(exception != (ExceptionInfo *) NULL);
536   assert(exception->signature == MagickCoreSignature);
537   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
538   if (status == MagickFalse)
539     return(status);
540   scene=0;
541 
542 
543   quantum_info=AcquireQuantumInfo(image_info,image);
544   if ((quantum_info->format == UndefinedQuantumFormat) &&
545       (IsHighDynamicRangeImage(image,exception) != MagickFalse))
546     SetQuantumFormat(image,quantum_info,FloatingPointQuantumFormat);
547   switch(quantum_info->depth){
548   case 8:
549     ipl_info.byteType = 0;
550     break;
551   case 16:
552     if(quantum_info->format == SignedQuantumFormat){
553       ipl_info.byteType = 2;
554     }
555     else{
556       ipl_info.byteType = 1;
557     }
558     break;
559   case 32:
560     if(quantum_info->format == FloatingPointQuantumFormat){
561       ipl_info.byteType = 3;
562     }
563     else{
564       ipl_info.byteType = 4;
565     }
566     break;
567   case 64:
568     ipl_info.byteType = 10;
569     break;
570   default:
571     ipl_info.byteType = 2;
572     break;
573 
574   }
575   ipl_info.z = (unsigned int) GetImageListLength(image);
576   /* There is no current method for detecting whether we have T or Z stacks */
577   ipl_info.time = 1;
578   ipl_info.width = (unsigned int) image->columns;
579   ipl_info.height = (unsigned int) image->rows;
580   (void) TransformImageColorspace(image,sRGBColorspace,exception);
581   if(IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) { ipl_info.colors = 3; }
582   else{ ipl_info.colors = 1; }
583 
584   ipl_info.size = (unsigned int) (28 +
585     ((image->depth)/8)*ipl_info.height*ipl_info.width*ipl_info.colors*ipl_info.z);
586 
587   /* Ok!  Calculations are done.  Lets write this puppy down! */
588 
589   /*
590     Write IPL header.
591   */
592   /* Shockingly (maybe not if you have used IPLab),  IPLab itself CANNOT read MSBEndian
593   files!   The reader above can, but they cannot.  For compatability reasons, I will leave
594   the code in here, but it is all but useless if you want to use IPLab. */
595 
596   if(image_info->endian == MSBEndian)
597     (void) WriteBlob(image, 4, (const unsigned char *) "mmmm");
598   else{
599     image->endian = LSBEndian;
600     (void) WriteBlob(image, 4, (const unsigned char *) "iiii");
601   }
602   (void) WriteBlobLong(image, 4);
603   (void) WriteBlob(image, 4, (const unsigned char *) "100f");
604   (void) WriteBlob(image, 4, (const unsigned char *) "data");
605   (void) WriteBlobLong(image, ipl_info.size);
606   (void) WriteBlobLong(image, ipl_info.width);
607   (void) WriteBlobLong(image, ipl_info.height);
608   (void) WriteBlobLong(image, ipl_info.colors);
609   if(image_info->adjoin == MagickFalse)
610   (void) WriteBlobLong(image, 1);
611   else
612   (void) WriteBlobLong(image, ipl_info.z);
613   (void) WriteBlobLong(image, ipl_info.time);
614   (void) WriteBlobLong(image, ipl_info.byteType);
615 
616   do
617     {
618       /*
619   Convert MIFF to IPL raster pixels.
620       */
621       pixels=(unsigned char *) GetQuantumPixels(quantum_info);
622   if(ipl_info.colors == 1){
623   /* Red frame */
624   for(y = 0; y < (ssize_t) ipl_info.height; y++){
625     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
626     if (p == (const Quantum *) NULL)
627       break;
628       (void) ExportQuantumPixels(image,(CacheView *) NULL, quantum_info,
629         GrayQuantum, pixels,exception);
630       (void) WriteBlob(image, image->columns*image->depth/8, pixels);
631   }
632 
633 }
634   if(ipl_info.colors == 3){
635   /* Red frame */
636   for(y = 0; y < (ssize_t) ipl_info.height; y++){
637     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
638     if (p == (const Quantum *) NULL)
639       break;
640       (void) ExportQuantumPixels(image,(CacheView *) NULL, quantum_info,
641         RedQuantum, pixels,exception);
642       (void) WriteBlob(image, image->columns*image->depth/8, pixels);
643   }
644 
645     /* Green frame */
646     for(y = 0; y < (ssize_t) ipl_info.height; y++){
647       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
648       if (p == (const Quantum *) NULL)
649         break;
650         (void) ExportQuantumPixels(image,(CacheView *) NULL, quantum_info,
651           GreenQuantum, pixels,exception);
652         (void) WriteBlob(image, image->columns*image->depth/8, pixels);
653     }
654     /* Blue frame */
655     for(y = 0; y < (ssize_t) ipl_info.height; y++){
656       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
657       if (p == (const Quantum *) NULL)
658         break;
659       (void) ExportQuantumPixels(image,(CacheView *) NULL, quantum_info,
660         BlueQuantum, pixels,exception);
661       (void) WriteBlob(image, image->columns*image->depth/8, pixels);
662       if (image->previous == (Image *) NULL)
663         {
664           status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
665                 image->rows);
666           if (status == MagickFalse)
667             break;
668         }
669     }
670   }
671   quantum_info=DestroyQuantumInfo(quantum_info);
672       if (GetNextImageInList(image) == (Image *) NULL)
673   break;
674       image=SyncNextImageInList(image);
675       status=SetImageProgress(image,SaveImagesTag,scene++,
676         GetImageListLength(image));
677       if (status == MagickFalse)
678         break;
679     }while (image_info->adjoin != MagickFalse);
680 
681   (void) WriteBlob(image, 4, (const unsigned char *) "fini");
682   (void) WriteBlobLong(image, 0);
683 
684 CloseBlob(image);
685 return(MagickTrue);
686 }
687