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