Home | History | Annotate | Download | only in coders
      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  */
    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 
    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 
    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  */
    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  */
    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 */
    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