Home | History | Annotate | Download | only in coders
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %                        V   V  IIIII  PPPP   SSSSS                           %
      7 %                        V   V    I    P   P  SS                              %
      8 %                        V   V    I    PPPP    SSS                            %
      9 %                         V V     I    P         SS                           %
     10 %                          V    IIIII  P      SSSSS                           %
     11 %                                                                             %
     12 %                                                                             %
     13 %                        Read/Write VIPS Image Format                         %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                Dirk Lemstra                                 %
     17 %                                 April 2014                                  %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %
     37 */
     38 
     39 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/attribute.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/pixel-accessor.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   Define declaractions.
     68 */
     69 #define VIPS_MAGIC_LSB 0x08f2a6b6U
     70 #define VIPS_MAGIC_MSB 0xb6a6f208U
     71 
     72 typedef enum
     73 {
     74   VIPSBandFormatNOTSET    = -1,
     75   VIPSBandFormatUCHAR     = 0,  /* Unsigned 8-bit int */
     76   VIPSBandFormatCHAR      = 1,  /* Signed 8-bit int */
     77   VIPSBandFormatUSHORT    = 2,  /* Unsigned 16-bit int */
     78   VIPSBandFormatSHORT     = 3,  /* Signed 16-bit int */
     79   VIPSBandFormatUINT      = 4,  /* Unsigned 32-bit int */
     80   VIPSBandFormatINT       = 5,  /* Signed 32-bit int */
     81   VIPSBandFormatFLOAT     = 6,  /* 32-bit IEEE float */
     82   VIPSBandFormatCOMPLEX   = 7,  /* Complex (2 floats) */
     83   VIPSBandFormatDOUBLE    = 8,  /* 64-bit IEEE double */
     84   VIPSBandFormatDPCOMPLEX = 9   /* Complex (2 doubles) */
     85 } VIPSBandFormat;
     86 
     87 typedef enum
     88 {
     89   VIPSCodingNONE  = 0,  /* VIPS computation format */
     90   VIPSCodingLABQ  = 2,  /* LABQ storage format */
     91   VIPSCodingRAD   = 6   /* Radiance storage format */
     92 } VIPSCoding;
     93 
     94 typedef enum
     95 {
     96   VIPSTypeMULTIBAND = 0,   /* Some multiband image */
     97   VIPSTypeB_W       = 1,   /* Some single band image */
     98   VIPSTypeHISTOGRAM = 10,  /* Histogram or LUT */
     99   VIPSTypeFOURIER   = 24,  /* Image in Fourier space */
    100   VIPSTypeXYZ       = 12,  /* CIE XYZ colour space */
    101   VIPSTypeLAB       = 13,  /* CIE LAB colour space */
    102   VIPSTypeCMYK      = 15,  /* im_icc_export() */
    103   VIPSTypeLABQ      = 16,  /* 32-bit CIE LAB */
    104   VIPSTypeRGB       = 17,  /* Some RGB */
    105   VIPSTypeUCS       = 18,  /* UCS(1:1) colour space */
    106   VIPSTypeLCH       = 19,  /* CIE LCh colour space */
    107   VIPSTypeLABS      = 21,  /* 48-bit CIE LAB */
    108   VIPSTypesRGB      = 22,  /* sRGB colour space */
    109   VIPSTypeYXY       = 23,  /* CIE Yxy colour space */
    110   VIPSTypeRGB16     = 25,  /* 16-bit RGB */
    111   VIPSTypeGREY16    = 26   /* 16-bit monochrome */
    112 } VIPSType;
    113 
    114 /*
    115   Forward declarations.
    116 */
    117 static MagickBooleanType
    118   WriteVIPSImage(const ImageInfo *,Image *,ExceptionInfo *);
    119 
    121 /*
    123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    124 %                                                                             %
    125 %                                                                             %
    126 %                                                                             %
    127 %   I s V I P S                                                               %
    128 %                                                                             %
    129 %                                                                             %
    130 %                                                                             %
    131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    132 %
    133 %  IsVIPS() returns MagickTrue if the image format type, identified by the
    134 %  magick string, is VIPS.
    135 %
    136 %  The format of the IsVIPS method is:
    137 %
    138 %      MagickBooleanType IsVIPS(const unsigned char *magick,const size_t length)
    139 %
    140 %  A description of each parameter follows:
    141 %
    142 %    o magick: compare image format pattern against these bytes.
    143 %
    144 %    o length: Specifies the length of the magick string.
    145 %
    146 */
    147 static MagickBooleanType IsVIPS(const unsigned char *magick,const size_t length)
    148 {
    149   if (length < 4)
    150     return(MagickFalse);
    151 
    152   if (memcmp(magick,"\010\362\246\266",4) == 0)
    153     return(MagickTrue);
    154 
    155   if (memcmp(magick,"\266\246\362\010",4) == 0)
    156     return(MagickTrue);
    157 
    158   return(MagickFalse);
    159 }
    160 
    161 /*
    162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    163 %                                                                             %
    164 %                                                                             %
    165 %                                                                             %
    166 %   R e a d V I P S I m a g e                                                 %
    167 %                                                                             %
    168 %                                                                             %
    169 %                                                                             %
    170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    171 %
    172 %  ReadVIPSImage() reads a VIPS image file and returns it. It allocates the
    173 %  memory necessary for the new Image structure and returns a pointer to the
    174 %  new image.
    175 %
    176 %  The format of the ReadVIPSImage method is:
    177 %
    178 %      Image *ReadVIPSmage(const ImageInfo *image_info,ExceptionInfo *exception)
    179 %
    180 %  A description of each parameter follows:
    181 %
    182 %    o image_info: the image info.
    183 %
    184 %    o exception: return any errors or warnings in this structure.
    185 %
    186 */
    187 
    188 static inline MagickBooleanType IsSupportedCombination(
    189   const VIPSBandFormat format,const VIPSType type)
    190 {
    191   switch(type)
    192   {
    193     case VIPSTypeB_W:
    194     case VIPSTypeCMYK:
    195     case VIPSTypeRGB:
    196     case VIPSTypesRGB:
    197       return(MagickTrue);
    198     case VIPSTypeGREY16:
    199     case VIPSTypeRGB16:
    200       switch(format)
    201       {
    202         case VIPSBandFormatUSHORT:
    203         case VIPSBandFormatSHORT:
    204         case VIPSBandFormatUINT:
    205         case VIPSBandFormatINT:
    206         case VIPSBandFormatFLOAT:
    207         case VIPSBandFormatDOUBLE:
    208           return(MagickTrue);
    209         default:
    210           return(MagickFalse);
    211       }
    212     default:
    213       return(MagickFalse);
    214   }
    215 }
    216 
    217 static inline Quantum ReadVIPSPixelNONE(Image *image,
    218   const VIPSBandFormat format,const VIPSType type)
    219 {
    220   switch(type)
    221   {
    222     case VIPSTypeB_W:
    223     case VIPSTypeRGB:
    224       {
    225         unsigned char
    226           c;
    227 
    228         switch(format)
    229         {
    230           case VIPSBandFormatUCHAR:
    231           case VIPSBandFormatCHAR:
    232             c=(unsigned char) ReadBlobByte(image);
    233             break;
    234           case VIPSBandFormatUSHORT:
    235           case VIPSBandFormatSHORT:
    236             c=(unsigned char) ReadBlobShort(image);
    237             break;
    238           case VIPSBandFormatUINT:
    239           case VIPSBandFormatINT:
    240             c=(unsigned char) ReadBlobLong(image);
    241             break;
    242           case VIPSBandFormatFLOAT:
    243             c=(unsigned char) ReadBlobFloat(image);
    244             break;
    245           case VIPSBandFormatDOUBLE:
    246             c=(unsigned char) ReadBlobDouble(image);
    247             break;
    248           default:
    249             c=0;
    250             break;
    251         }
    252         return(ScaleCharToQuantum(c));
    253       }
    254     case VIPSTypeGREY16:
    255     case VIPSTypeRGB16:
    256       {
    257         unsigned short
    258           s;
    259 
    260         switch(format)
    261         {
    262           case VIPSBandFormatUSHORT:
    263           case VIPSBandFormatSHORT:
    264             s=(unsigned short) ReadBlobShort(image);
    265             break;
    266           case VIPSBandFormatUINT:
    267           case VIPSBandFormatINT:
    268             s=(unsigned short) ReadBlobLong(image);
    269             break;
    270           case VIPSBandFormatFLOAT:
    271             s=(unsigned short) ReadBlobFloat(image);
    272             break;
    273           case VIPSBandFormatDOUBLE:
    274             s=(unsigned short) ReadBlobDouble(image);
    275             break;
    276           default:
    277             s=0;
    278             break;
    279         }
    280         return(ScaleShortToQuantum(s));
    281       }
    282     case VIPSTypeCMYK:
    283     case VIPSTypesRGB:
    284       switch(format)
    285       {
    286         case VIPSBandFormatUCHAR:
    287         case VIPSBandFormatCHAR:
    288           return(ScaleCharToQuantum((unsigned char) ReadBlobByte(image)));
    289         case VIPSBandFormatUSHORT:
    290         case VIPSBandFormatSHORT:
    291           return(ScaleShortToQuantum(ReadBlobShort(image)));
    292         case VIPSBandFormatUINT:
    293         case VIPSBandFormatINT:
    294           return(ScaleLongToQuantum(ReadBlobLong(image)));
    295         case VIPSBandFormatFLOAT:
    296           return((Quantum) ((float) QuantumRange*(ReadBlobFloat(image)/1.0)));
    297         case VIPSBandFormatDOUBLE:
    298           return((Quantum) ((double) QuantumRange*(ReadBlobDouble(
    299             image)/1.0)));
    300         default:
    301           return((Quantum) 0);
    302       }
    303     default:
    304       return((Quantum) 0);
    305   }
    306 }
    307 
    308 static MagickBooleanType ReadVIPSPixelsNONE(Image *image,
    309   const VIPSBandFormat format,const VIPSType type,const unsigned int channels,
    310   ExceptionInfo *exception)
    311 {
    312   Quantum
    313     pixel;
    314 
    315   register Quantum
    316     *q;
    317 
    318   register ssize_t
    319     x;
    320 
    321   ssize_t
    322     y;
    323 
    324   for (y = 0; y < (ssize_t) image->rows; y++)
    325   {
    326     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
    327     if (q == (Quantum *) NULL)
    328       return MagickFalse;
    329     for (x=0; x < (ssize_t) image->columns; x++)
    330     {
    331       pixel=ReadVIPSPixelNONE(image,format,type);
    332       SetPixelRed(image,pixel,q);
    333       if (channels < 3)
    334         {
    335           SetPixelGreen(image,pixel,q);
    336           SetPixelBlue(image,pixel,q);
    337           if (channels == 2)
    338             SetPixelAlpha(image,ReadVIPSPixelNONE(image,format,type),q);
    339         }
    340       else
    341         {
    342           SetPixelGreen(image,ReadVIPSPixelNONE(image,format,type),q);
    343           SetPixelBlue(image,ReadVIPSPixelNONE(image,format,type),q);
    344           if (channels == 4)
    345             {
    346               if (image->colorspace == CMYKColorspace)
    347                 SetPixelIndex(image,ReadVIPSPixelNONE(image,format,type),q);
    348               else
    349                 SetPixelAlpha(image,ReadVIPSPixelNONE(image,format,type),q);
    350             }
    351           else if (channels == 5)
    352             {
    353               SetPixelIndex(image,ReadVIPSPixelNONE(image,format,type),q);
    354               SetPixelAlpha(image,ReadVIPSPixelNONE(image,format,type),q);
    355             }
    356         }
    357       q+=GetPixelChannels(image);
    358     }
    359     if (SyncAuthenticPixels(image,exception) == MagickFalse)
    360       return MagickFalse;
    361   }
    362   return(MagickTrue);
    363 }
    364 
    365 static Image *ReadVIPSImage(const ImageInfo *image_info,
    366   ExceptionInfo *exception)
    367 {
    368   char
    369     buffer[MagickPathExtent],
    370     *metadata;
    371 
    372   Image
    373     *image;
    374 
    375   MagickBooleanType
    376     status;
    377 
    378   ssize_t
    379     n;
    380 
    381   unsigned int
    382     channels,
    383     marker;
    384 
    385   VIPSBandFormat
    386     format;
    387 
    388   VIPSCoding
    389     coding;
    390 
    391   VIPSType
    392     type;
    393 
    394   assert(image_info != (const ImageInfo *) NULL);
    395   assert(image_info->signature == MagickCoreSignature);
    396   if (image_info->debug != MagickFalse)
    397     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    398       image_info->filename);
    399   assert(exception != (ExceptionInfo *) NULL);
    400   assert(exception->signature == MagickCoreSignature);
    401 
    402   image=AcquireImage(image_info,exception);
    403   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    404   if (status == MagickFalse)
    405     {
    406       image=DestroyImageList(image);
    407       return((Image *) NULL);
    408     }
    409   marker=ReadBlobLSBLong(image);
    410   if (marker == VIPS_MAGIC_LSB)
    411     image->endian=LSBEndian;
    412   else if (marker == VIPS_MAGIC_MSB)
    413     image->endian=MSBEndian;
    414   else
    415     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    416   image->columns=(size_t) ReadBlobLong(image);
    417   image->rows=(size_t) ReadBlobLong(image);
    418   status=SetImageExtent(image,image->columns,image->rows,exception);
    419   if (status == MagickFalse)
    420     return(DestroyImageList(image));
    421   channels=ReadBlobLong(image);
    422   (void) ReadBlobLong(image); /* Legacy */
    423   format=(VIPSBandFormat) ReadBlobLong(image);
    424   switch(format)
    425   {
    426     case VIPSBandFormatUCHAR:
    427     case VIPSBandFormatCHAR:
    428       image->depth=8;
    429       break;
    430     case VIPSBandFormatUSHORT:
    431     case VIPSBandFormatSHORT:
    432       image->depth=16;
    433       break;
    434     case VIPSBandFormatUINT:
    435     case VIPSBandFormatINT:
    436     case VIPSBandFormatFLOAT:
    437       image->depth=32;
    438       break;
    439     case VIPSBandFormatDOUBLE:
    440       image->depth=64;
    441       break;
    442     default:
    443     case VIPSBandFormatCOMPLEX:
    444     case VIPSBandFormatDPCOMPLEX:
    445     case VIPSBandFormatNOTSET:
    446       ThrowReaderException(CoderError,"Unsupported band format");
    447   }
    448   coding=(VIPSCoding) ReadBlobLong(image);
    449   type=(VIPSType) ReadBlobLong(image);
    450   switch(type)
    451   {
    452     case VIPSTypeCMYK:
    453       SetImageColorspace(image,CMYKColorspace,exception);
    454       if (channels == 5)
    455         image->alpha_trait=BlendPixelTrait;
    456       break;
    457     case VIPSTypeB_W:
    458     case VIPSTypeGREY16:
    459       SetImageColorspace(image,GRAYColorspace,exception);
    460       if (channels == 2)
    461         image->alpha_trait=BlendPixelTrait;
    462       break;
    463     case VIPSTypeRGB:
    464     case VIPSTypeRGB16:
    465       SetImageColorspace(image,RGBColorspace,exception);
    466       if (channels == 4)
    467         image->alpha_trait=BlendPixelTrait;
    468       break;
    469     case VIPSTypesRGB:
    470       SetImageColorspace(image,sRGBColorspace,exception);
    471       if (channels == 4)
    472         image->alpha_trait=BlendPixelTrait;
    473       break;
    474     default:
    475     case VIPSTypeFOURIER:
    476     case VIPSTypeHISTOGRAM:
    477     case VIPSTypeLAB:
    478     case VIPSTypeLABS:
    479     case VIPSTypeLABQ:
    480     case VIPSTypeLCH:
    481     case VIPSTypeMULTIBAND:
    482     case VIPSTypeUCS:
    483     case VIPSTypeXYZ:
    484     case VIPSTypeYXY:
    485       ThrowReaderException(CoderError,"Unsupported colorspace");
    486   }
    487   image->units=PixelsPerCentimeterResolution;
    488   image->resolution.x=ReadBlobFloat(image)*10;
    489   image->resolution.y=ReadBlobFloat(image)*10;
    490   /*
    491     Legacy, offsets, future
    492   */
    493   (void) ReadBlobLongLong(image);
    494   (void) ReadBlobLongLong(image);
    495   (void) ReadBlobLongLong(image);
    496   if (image_info->ping != MagickFalse)
    497     return(image);
    498   if (IsSupportedCombination(format,type) == MagickFalse)
    499     ThrowReaderException(CoderError,
    500       "Unsupported combination of band format and colorspace");
    501   if (channels == 0 || channels > 5)
    502     ThrowReaderException(CoderError,"Unsupported number of channels");
    503   if (coding == VIPSCodingNONE)
    504     status=ReadVIPSPixelsNONE(image,format,type,channels,exception);
    505   else
    506     ThrowReaderException(CoderError,"Unsupported coding");
    507   metadata=(char *) NULL;
    508   while ((n=ReadBlob(image,MagickPathExtent-1,(unsigned char *) buffer)) != 0)
    509   {
    510     buffer[n]='\0';
    511     if (metadata == (char *) NULL)
    512       metadata=ConstantString(buffer);
    513     else
    514       (void) ConcatenateString(&metadata,buffer);
    515   }
    516   if (metadata != (char *) NULL)
    517     SetImageProperty(image,"vips:metadata",metadata,exception);
    518   (void) CloseBlob(image);
    519   if (status == MagickFalse)
    520     return((Image *) NULL);
    521   return(image);
    522 }
    523 
    524 /*
    526 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    527 %                                                                             %
    528 %                                                                             %
    529 %                                                                             %
    530 %   R e g i s t e r V I P S I m a g e                                         %
    531 %                                                                             %
    532 %                                                                             %
    533 %                                                                             %
    534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    535 %
    536 %  RegisterVIPSmage() adds attributes for the VIPS image format to the list
    537 %  of supported formats.  The attributes include the image format tag, a
    538 %  method to read and/or write the format, whether the format supports the
    539 %  saving of more than one frame to the same file or blob, whether the format
    540 %  supports native in-memory I/O, and a brief description of the format.
    541 %
    542 %  The format of the RegisterVIPSImage method is:
    543 %
    544 %      size_t RegisterVIPSImage(void)
    545 %
    546 */
    547 ModuleExport size_t RegisterVIPSImage(void)
    548 {
    549   MagickInfo
    550     *entry;
    551 
    552   entry=AcquireMagickInfo("VIPS","VIPS","VIPS image");
    553   entry->decoder=(DecodeImageHandler *) ReadVIPSImage;
    554   entry->encoder=(EncodeImageHandler *) WriteVIPSImage;
    555   entry->magick=(IsImageFormatHandler *) IsVIPS;
    556   entry->flags|=CoderEndianSupportFlag;
    557   (void) RegisterMagickInfo(entry);
    558   return(MagickImageCoderSignature);
    559 }
    560 
    561 /*
    563 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    564 %                                                                             %
    565 %                                                                             %
    566 %                                                                             %
    567 %   U n r e g i s t e r V I P S I m a g e                                     %
    568 %                                                                             %
    569 %                                                                             %
    570 %                                                                             %
    571 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    572 %
    573 %  UnregisterVIPSImage() removes format registrations made by the
    574 %  VIPS module from the list of supported formats.
    575 %
    576 %  The format of the UnregisterVIPSImage method is:
    577 %
    578 %      UnregisterVIPSImage(void)
    579 %
    580 */
    581 ModuleExport void UnregisterVIPSImage(void)
    582 {
    583   (void) UnregisterMagickInfo("VIPS");
    584 }
    585 
    586 /*
    588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    589 %                                                                             %
    590 %                                                                             %
    591 %                                                                             %
    592 %   W r i t e V I P S I m a g e                                               %
    593 %                                                                             %
    594 %                                                                             %
    595 %                                                                             %
    596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    597 %
    598 %  WriteVIPSImage() writes an image to a file in VIPS image format.
    599 %
    600 %  The format of the WriteVIPSImage method is:
    601 %
    602 %      MagickBooleanType WriteVIPSImage(const ImageInfo *image_info,Image *image)
    603 %
    604 %  A description of each parameter follows.
    605 %
    606 %    o image_info: the image info.
    607 %
    608 %    o image:  The image.
    609 %
    610 */
    611 
    612 static inline void WriteVIPSPixel(Image *image, const Quantum value)
    613 {
    614   if (image->depth == 16)
    615     (void) WriteBlobShort(image,ScaleQuantumToShort(value));
    616   else
    617     (void) WriteBlobByte(image,ScaleQuantumToChar(value));
    618 }
    619 
    620 static MagickBooleanType WriteVIPSImage(const ImageInfo *image_info,
    621   Image *image,ExceptionInfo *exception)
    622 {
    623   const char
    624     *metadata;
    625 
    626   MagickBooleanType
    627     status;
    628 
    629   register const Quantum
    630     *p;
    631 
    632   register ssize_t
    633     x;
    634 
    635   ssize_t
    636     y;
    637 
    638   unsigned int
    639     channels;
    640 
    641   assert(image_info != (const ImageInfo *) NULL);
    642   assert(image_info->signature == MagickCoreSignature);
    643   assert(image != (Image *) NULL);
    644   assert(image->signature == MagickCoreSignature);
    645   if (image->debug != MagickFalse)
    646     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    647 
    648   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    649   if (status == MagickFalse)
    650     return(status);
    651   if (image->endian == LSBEndian)
    652     (void) WriteBlobLSBLong(image,VIPS_MAGIC_LSB);
    653   else
    654     (void) WriteBlobLSBLong(image,VIPS_MAGIC_MSB);
    655   (void) WriteBlobLong(image,(unsigned int) image->columns);
    656   (void) WriteBlobLong(image,(unsigned int) image->rows);
    657   (void) SetImageStorageClass(image,DirectClass,exception);
    658   channels=image->alpha_trait != UndefinedPixelTrait ? 4 : 3;
    659   if (SetImageGray(image,exception) != MagickFalse)
    660     channels=image->alpha_trait != UndefinedPixelTrait ? 2 : 1;
    661   else if (image->colorspace == CMYKColorspace)
    662     channels=image->alpha_trait != UndefinedPixelTrait ? 5 : 4;
    663   (void) WriteBlobLong(image,channels);
    664   (void) WriteBlobLong(image,0);
    665   if (image->depth == 16)
    666     (void) WriteBlobLong(image,(unsigned int) VIPSBandFormatUSHORT);
    667   else
    668     {
    669       image->depth=8;
    670       (void) WriteBlobLong(image,(unsigned int) VIPSBandFormatUCHAR);
    671     }
    672   (void) WriteBlobLong(image,VIPSCodingNONE);
    673   switch(image->colorspace)
    674   {
    675     case CMYKColorspace:
    676       (void) WriteBlobLong(image,VIPSTypeCMYK);
    677       break;
    678     case GRAYColorspace:
    679       if (image->depth == 16)
    680         (void) WriteBlobLong(image, VIPSTypeGREY16);
    681       else
    682         (void) WriteBlobLong(image, VIPSTypeB_W);
    683       break;
    684     case LabColorspace:
    685       (void) WriteBlobLong(image,VIPSTypeLAB);
    686       break;
    687     case LCHColorspace:
    688       (void) WriteBlobLong(image,VIPSTypeLCH);
    689       break;
    690     case RGBColorspace:
    691       if (image->depth == 16)
    692         (void) WriteBlobLong(image, VIPSTypeRGB16);
    693       else
    694         (void) WriteBlobLong(image, VIPSTypeRGB);
    695       break;
    696     case XYZColorspace:
    697       (void) WriteBlobLong(image,VIPSTypeXYZ);
    698       break;
    699     default:
    700     case sRGBColorspace:
    701       (void) SetImageColorspace(image,sRGBColorspace,exception);
    702       (void) WriteBlobLong(image,VIPSTypesRGB);
    703       break;
    704   }
    705   if (image->units == PixelsPerCentimeterResolution)
    706     {
    707       (void) WriteBlobFloat(image,(image->resolution.x / 10));
    708       (void) WriteBlobFloat(image,(image->resolution.y / 10));
    709     }
    710   else if (image->units == PixelsPerInchResolution)
    711     {
    712       (void) WriteBlobFloat(image,(image->resolution.x / 25.4));
    713       (void) WriteBlobFloat(image,(image->resolution.y / 25.4));
    714     }
    715   else
    716     {
    717       (void) WriteBlobLong(image,0);
    718       (void) WriteBlobLong(image,0);
    719     }
    720   /*
    721     Legacy, Offsets, Future
    722   */
    723   for (y=0; y < 24; y++)
    724     (void) WriteBlobByte(image,0);
    725   for (y=0; y < (ssize_t) image->rows; y++)
    726   {
    727     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    728     if (p == (const Quantum *) NULL)
    729       break;
    730     for (x=0; x < (ssize_t) image->columns; x++)
    731     {
    732       WriteVIPSPixel(image,GetPixelRed(image,p));
    733       if (channels == 2)
    734         WriteVIPSPixel(image,GetPixelAlpha(image,p));
    735       else
    736         {
    737           WriteVIPSPixel(image,GetPixelGreen(image,p));
    738           WriteVIPSPixel(image,GetPixelBlue(image,p));
    739           if (channels >= 4)
    740             {
    741               if (image->colorspace == CMYKColorspace)
    742                 WriteVIPSPixel(image,GetPixelIndex(image,p));
    743               else
    744                 WriteVIPSPixel(image,GetPixelAlpha(image,p));
    745             }
    746           else if (channels == 5)
    747             {
    748                WriteVIPSPixel(image,GetPixelIndex(image,p));
    749                WriteVIPSPixel(image,GetPixelAlpha(image,p));
    750             }
    751         }
    752       p+=GetPixelChannels(image);
    753     }
    754   }
    755   metadata=GetImageProperty(image,"vips:metadata",exception);
    756   if (metadata != (const char*) NULL)
    757     WriteBlobString(image,metadata);
    758   (void) CloseBlob(image);
    759   return(status);
    760 }
    761