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