blob: 844cec53958a115dd946a0dd63aeb599f1515eff [file] [log] [blame]
dirke3039842014-04-26 18:22:06 +00001/*
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-2014 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/*
40 Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/colorspace.h"
48#include "MagickCore/colorspace-private.h"
49#include "MagickCore/exception.h"
50#include "MagickCore/exception-private.h"
51#include "MagickCore/image.h"
52#include "MagickCore/image-private.h"
53#include "MagickCore/list.h"
54#include "MagickCore/magick.h"
55#include "MagickCore/memory_.h"
56#include "MagickCore/monitor.h"
57#include "MagickCore/monitor-private.h"
58#include "MagickCore/pixel-accessor.h"
59#include "MagickCore/property.h"
60#include "MagickCore/quantum-private.h"
61#include "MagickCore/static.h"
62#include "MagickCore/string_.h"
63#include "MagickCore/module.h"
64
65/*
66 Define declaractions.
67*/
68#define VIPS_MAGIC_LSB 0x08f2a6b6U
69#define VIPS_MAGIC_MSB 0xb6a6f208U
70
71typedef enum
72{
73 VIPSBandFormatNOTSET = -1,
74 VIPSBandFormatUCHAR = 0, /* Unsigned 8-bit int */
75 VIPSBandFormatCHAR = 1, /* Signed 8-bit int */
76 VIPSBandFormatUSHORT = 2, /* Unsigned 16-bit int */
77 VIPSBandFormatSHORT = 3, /* Signed 16-bit int */
78 VIPSBandFormatUINT = 4, /* Unsigned 32-bit int */
79 VIPSBandFormatINT = 5, /* Signed 32-bit int */
80 VIPSBandFormatFLOAT = 6, /* 32-bit IEEE float */
81 VIPSBandFormatCOMPLEX = 7, /* Complex (2 floats) */
82 VIPSBandFormatDOUBLE = 8, /* 64-bit IEEE double */
83 VIPSBandFormatDPCOMPLEX = 9 /* Complex (2 doubles) */
84} VIPSBandFormat;
85
86typedef enum
87{
88 VIPSCodingNONE = 0, /* VIPS computation format */
89 VIPSCodingLABQ = 2, /* LABQ storage format */
90 VIPSCodingRAD = 6 /* Radiance storage format */
91} VIPSCoding;
92
93typedef enum
94{
95 VIPSTypeMULTIBAND = 0, /* Some multiband image */
96 VIPSTypeB_W = 1, /* Some single band image */
97 VIPSTypeHISTOGRAM = 10, /* Histogram or LUT */
98 VIPSTypeFOURIER = 24, /* Image in Fourier space */
99 VIPSTypeXYZ = 12, /* CIE XYZ colour space */
100 VIPSTypeLAB = 13, /* CIE LAB colour space */
101 VIPSTypeCMYK = 15, /* im_icc_export() */
102 VIPSTypeLABQ = 16, /* 32-bit CIE LAB */
103 VIPSTypeRGB = 17, /* Some RGB */
104 VIPSTypeUCS = 18, /* UCS(1:1) colour space */
105 VIPSTypeLCH = 19, /* CIE LCh colour space */
106 VIPSTypeLABS = 21, /* 48-bit CIE LAB */
107 VIPSTypesRGB = 22, /* sRGB colour space */
108 VIPSTypeYXY = 23, /* CIE Yxy colour space */
109 VIPSTypeRGB16 = 25, /* 16-bit RGB */
110 VIPSTypeGREY16 = 26 /* 16-bit monochrome */
111} VIPSType;
112
113/*
114 Forward declarations.
115*/
116static MagickBooleanType
117 WriteVIPSImage(const ImageInfo *,Image *,ExceptionInfo *);
118
119/*
120%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121% %
122% %
123% %
124% I s V I P S %
125% %
126% %
127% %
128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129%
130% IsVIPS() returns MagickTrue if the image format type, identified by the
131% magick string, is VIPS.
132%
133% The format of the IsVIPS method is:
134%
135% MagickBooleanType IsVIPS(const unsigned char *magick,const size_t length)
136%
137% A description of each parameter follows:
138%
139% o magick: compare image format pattern against these bytes.
140%
141% o length: Specifies the length of the magick string.
142%
143*/
144static MagickBooleanType IsVIPS(const unsigned char *magick,const size_t length)
145{
146 if (length < 4)
147 return(MagickFalse);
148
149 if (memcmp(magick,"\010\362\246\266",4) == 0)
150 return(MagickTrue);
151
152 if (memcmp(magick,"\266\246\362\010",4) == 0)
153 return(MagickTrue);
154
155 return(MagickFalse);
156}
157
158/*
159%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
160% %
161% %
162% %
163% R e a d V I P S I m a g e %
164% %
165% %
166% %
167%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168%
169% ReadVIPSImage() reads a VIPS image file and returns it. It allocates the
170% memory necessary for the new Image structure and returns a pointer to the
171% new image.
172%
173% The format of the ReadVIPSImage method is:
174%
175% Image *ReadVIPSmage(const ImageInfo *image_info,ExceptionInfo *exception)
176%
177% A description of each parameter follows:
178%
179% o image_info: the image info.
180%
181% o exception: return any errors or warnings in this structure.
182%
183*/
184
185static inline Quantum ReadVIPSPixelNONE(Image *image,const VIPSBandFormat format)
186{
187 switch(format)
188 {
189 case VIPSBandFormatUCHAR:
190 case VIPSBandFormatCHAR:
191 return(ScaleCharToQuantum((unsigned char) ReadBlobByte(image)));
192 case VIPSBandFormatUSHORT:
193 case VIPSBandFormatSHORT:
194 return(ScaleShortToQuantum(ReadBlobShort(image)));
195 case VIPSBandFormatUINT:
196 case VIPSBandFormatINT:
197 return(ScaleLongToQuantum(ReadBlobLong(image)));
198 case VIPSBandFormatFLOAT:
199 return(ScaleMapToQuantum((MagickRealType) ReadBlobFloat(image)));
200 case VIPSBandFormatDOUBLE:
201 return(ScaleMapToQuantum((MagickRealType) ReadBlobDouble(image)));
202 default:
203 return((Quantum) 0);
204 }
205}
206
207static MagickBooleanType ReadVIPSPixelsNONE(Image *image,
208 const VIPSBandFormat format,const unsigned int channels,
209 ExceptionInfo *exception)
210{
211 Quantum
212 pixel;
213
214 register Quantum
215 *q;
216
217 register ssize_t
218 x;
219
220 ssize_t
221 y;
222
223 for (y = 0; y < (ssize_t) image->rows; y++)
224 {
225 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
226 if (q == (Quantum *) NULL)
227 return MagickFalse;
228 for (x=0; x < (ssize_t) image->columns; x++)
229 {
230 pixel=ReadVIPSPixelNONE(image,format);
231 SetPixelRed(image,pixel,q);
232 if (channels < 3)
233 {
234 SetPixelGreen(image,pixel,q);
235 SetPixelBlue(image,pixel,q);
236 if (channels == 2)
237 SetPixelAlpha(image,ReadVIPSPixelNONE(image,format),q);
238 }
239 else
240 {
241 SetPixelGreen(image,ReadVIPSPixelNONE(image,format),q);
242 SetPixelBlue(image,ReadVIPSPixelNONE(image,format),q);
243 if (channels == 4)
244 {
245 if (image->colorspace == CMYKColorspace)
246 SetPixelIndex(image,ReadVIPSPixelNONE(image,format),q);
247 else
248 SetPixelAlpha(image,ReadVIPSPixelNONE(image,format),q);
249 }
250 else if (channels == 5)
251 {
252 SetPixelIndex(image,ReadVIPSPixelNONE(image,format),q);
253 SetPixelAlpha(image,ReadVIPSPixelNONE(image,format),q);
254 }
255 }
256 q+=GetPixelChannels(image);
257 }
258 if (SyncAuthenticPixels(image,exception) == MagickFalse)
259 return MagickFalse;
260 }
261 return(MagickTrue);
262}
263
264static Image *ReadVIPSImage(const ImageInfo *image_info,
265 ExceptionInfo *exception)
266{
267 char
268 buffer[MaxTextExtent],
269 *metadata;
270
271 Image
272 *image;
273
274 MagickBooleanType
275 status;
276
277 ssize_t
278 n;
279
280 unsigned int
281 channels,
282 marker;
283
284 VIPSBandFormat
285 format;
286
287 VIPSCoding
288 coding;
289
290 VIPSType
291 type;
292
293 assert(image_info != (const ImageInfo *) NULL);
294 assert(image_info->signature == MagickSignature);
295 if (image_info->debug != MagickFalse)
296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
297 image_info->filename);
298 assert(exception != (ExceptionInfo *) NULL);
299 assert(exception->signature == MagickSignature);
300
301 image=AcquireImage(image_info,exception);
302 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
303 if (status == MagickFalse)
304 {
305 image=DestroyImageList(image);
306 return((Image *) NULL);
307 }
308 marker=ReadBlobLSBLong(image);
309 if (marker == VIPS_MAGIC_LSB)
310 image->endian=LSBEndian;
311 else if (marker == VIPS_MAGIC_MSB)
312 image->endian=MSBEndian;
313 else
314 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
315 image->columns=(size_t) ReadBlobLong(image);
316 image->rows=(size_t) ReadBlobLong(image);
317 channels=ReadBlobLong(image);
318 (void) ReadBlobLong(image); /* Legacy */
319 format=(VIPSBandFormat) ReadBlobLong(image);
320 switch(format)
321 {
322 case VIPSBandFormatUCHAR:
323 case VIPSBandFormatCHAR:
324 image->depth=8;
325 break;
326 case VIPSBandFormatUSHORT:
327 case VIPSBandFormatSHORT:
328 image->depth=16;
329 break;
330 case VIPSBandFormatUINT:
331 case VIPSBandFormatINT:
332 case VIPSBandFormatFLOAT:
333 image->depth=32;
334 break;
335 case VIPSBandFormatDOUBLE:
336 image->depth=64;
337 break;
338 case VIPSBandFormatCOMPLEX:
339 case VIPSBandFormatDPCOMPLEX:
340 case VIPSBandFormatNOTSET:
341 ThrowReaderException(CorruptImageError,"Unsupported VIPS band format");
342 break;
343 }
344 coding=(VIPSCoding) ReadBlobLong(image);
345 type=(VIPSType) ReadBlobLong(image);
346 switch(type)
347 {
348 case VIPSTypeCMYK:
349 SetImageColorspace(image,CMYKColorspace,exception);
350 if (channels == 5)
351 image->alpha_trait=BlendPixelTrait;
352 break;
353 case VIPSTypeB_W:
354 case VIPSTypeGREY16:
355 SetImageColorspace(image,GRAYColorspace,exception);
356 if (channels == 2)
357 image->alpha_trait=BlendPixelTrait;
358 break;
359 case VIPSTypeLAB:
360 case VIPSTypeLABS:
361 case VIPSTypeLABQ:
362 SetImageColorspace(image,LabColorspace,exception);
363 break;
364 case VIPSTypeLCH:
365 SetImageColorspace(image,LCHColorspace,exception);
366 break;
367 case VIPSTypeRGB:
368 case VIPSTypeRGB16:
369 SetImageColorspace(image,RGBColorspace,exception);
370 if (channels == 4)
371 image->alpha_trait=BlendPixelTrait;
372 break;
373 case VIPSTypesRGB:
374 SetImageColorspace(image,sRGBColorspace,exception);
375 if (channels == 4)
376 image->alpha_trait=BlendPixelTrait;
377 break;
378 case VIPSTypeXYZ:
379 SetImageColorspace(image,XYZColorspace,exception);
380 break;
381 default:
382 case VIPSTypeFOURIER:
383 case VIPSTypeHISTOGRAM:
384 case VIPSTypeMULTIBAND:
385 case VIPSTypeUCS:
386 case VIPSTypeYXY:
387 ThrowReaderException(CorruptImageError,"Unsupported VIPS colorspace");
388 break;
389 }
390 image->units=PixelsPerCentimeterResolution;
391 image->resolution.x=ReadBlobFloat(image)*10;
392 image->resolution.y=ReadBlobFloat(image)*10;
393 /*
394 Legacy, offsets, future
395 */
396 (void) ReadBlobLongLong(image);
397 (void) ReadBlobLongLong(image);
398 (void) ReadBlobLongLong(image);
399 if (image_info->ping != MagickFalse)
400 return(image);
401 if (coding == VIPSCodingNONE)
402 status=ReadVIPSPixelsNONE(image,format,channels,exception);
403 else
404 ThrowReaderException(CorruptImageError,"Unsupported VIPS coding");
405 metadata=(char *) NULL;
406 while ((n=ReadBlob(image,MaxTextExtent-1,(unsigned char *) buffer)) != 0)
407 {
408 buffer[n]='\0';
409 if (metadata == (char *) NULL)
410 metadata=ConstantString(buffer);
411 else
412 (void) ConcatenateString(&metadata,buffer);
413 }
414 if (metadata != (char *) NULL)
415 SetImageProperty(image,"vips:metadata",metadata,exception);
416 (void) CloseBlob(image);
417 if (status == MagickFalse)
418 return((Image *) NULL);
419 return(image);
420}
421
422/*
423%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
424% %
425% %
426% %
427% R e g i s t e r V I P S I m a g e %
428% %
429% %
430% %
431%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
432%
433% RegisterVIPSmage() adds attributes for the VIPS image format to the list
434% of supported formats. The attributes include the image format tag, a
435% method to read and/or write the format, whether the format supports the
436% saving of more than one frame to the same file or blob, whether the format
437% supports native in-memory I/O, and a brief description of the format.
438%
439% The format of the RegisterVIPSImage method is:
440%
441% size_t RegisterVIPSImage(void)
442%
443*/
444ModuleExport size_t RegisterVIPSImage(void)
445{
446 MagickInfo
447 *entry;
448
449 entry=SetMagickInfo("VIPS");
450 entry->decoder=(DecodeImageHandler *) ReadVIPSImage;
451 entry->encoder=(EncodeImageHandler *) WriteVIPSImage;
452 entry->magick=(IsImageFormatHandler *) IsVIPS;
453 entry->description=ConstantString("VIPS image");
454 entry->endian_support=MagickTrue;
455 entry->module=ConstantString("VIPS");
456 (void) RegisterMagickInfo(entry);
457 return(MagickImageCoderSignature);
458}
459
460/*
461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
462% %
463% %
464% %
465% U n r e g i s t e r V I P S I m a g e %
466% %
467% %
468% %
469%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470%
471% UnregisterVIPSImage() removes format registrations made by the
472% VIPS module from the list of supported formats.
473%
474% The format of the UnregisterVIPSImage method is:
475%
476% UnregisterVIPSImage(void)
477%
478*/
479ModuleExport void UnregisterVIPSImage(void)
480{
481 (void) UnregisterMagickInfo("VIPS");
482}
483
484/*
485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486% %
487% %
488% %
489% W r i t e V I P S I m a g e %
490% %
491% %
492% %
493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
494%
495% WriteVIPSImage() writes an image to a file in VIPS image format.
496%
497% The format of the WriteVIPSImage method is:
498%
499% MagickBooleanType WriteVIPSImage(const ImageInfo *image_info,Image *image)
500%
501% A description of each parameter follows.
502%
503% o image_info: the image info.
504%
505% o image: The image.
506%
507*/
508
509static inline void WriteVIPSPixel(Image *image, const Quantum value)
510{
511 if (image->depth == 16)
512 (void) WriteBlobShort(image,ScaleQuantumToShort(value));
513 else
514 (void) WriteBlobByte(image,ScaleQuantumToChar(value));
515}
516
517static MagickBooleanType WriteVIPSImage(const ImageInfo *image_info,
518 Image *image,ExceptionInfo *exception)
519{
520 const char
521 *metadata;
522
523 MagickBooleanType
524 status;
525
526 register const Quantum
527 *p;
528
529 register ssize_t
530 x;
531
532 ssize_t
533 y;
534
535 unsigned int
536 channels;
537
538 assert(image_info != (const ImageInfo *) NULL);
539 assert(image_info->signature == MagickSignature);
540 assert(image != (Image *) NULL);
541 assert(image->signature == MagickSignature);
542 if (image->debug != MagickFalse)
543 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
544
545 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
546 if (status == MagickFalse)
547 return(status);
548 if (image->endian == LSBEndian)
549 (void) WriteBlobLSBLong(image,VIPS_MAGIC_LSB);
550 else
551 (void) WriteBlobLSBLong(image,VIPS_MAGIC_MSB);
552 (void) WriteBlobLong(image,image->columns);
553 (void) WriteBlobLong(image,image->rows);
554 (void) SetImageStorageClass(image,DirectClass,exception);
555 channels=image->alpha_trait == BlendPixelTrait ? 4 : 3;
556 if (IsImageGray(image,exception) != MagickFalse)
557 channels=image->alpha_trait == BlendPixelTrait ? 2 : 1;
558 else if (image->colorspace == CMYKColorspace)
559 channels=image->alpha_trait == BlendPixelTrait ? 5 : 4;
560 (void) WriteBlobLong(image,channels);
561 (void) WriteBlobLong(image,0);
562 if (image->depth == 16)
563 (void) WriteBlobLong(image,(unsigned int) VIPSBandFormatUSHORT);
564 else
565 {
566 image->depth=8;
567 (void) WriteBlobLong(image,(unsigned int) VIPSBandFormatUCHAR);
568 }
569 (void) WriteBlobLong(image,VIPSCodingNONE);
570 switch(image->colorspace)
571 {
572 case CMYKColorspace:
573 (void) WriteBlobLong(image,VIPSTypeCMYK);
574 break;
575 case GRAYColorspace:
576 if (image->depth == 16)
577 (void) WriteBlobLong(image, VIPSTypeGREY16);
578 else
579 (void) WriteBlobLong(image, VIPSTypeB_W);
580 break;
581 case LabColorspace:
582 (void) WriteBlobLong(image,VIPSTypeLAB);
583 break;
584 case LCHColorspace:
585 (void) WriteBlobLong(image,VIPSTypeLCH);
586 break;
587 case RGBColorspace:
588 if (image->depth == 16)
589 (void) WriteBlobLong(image, VIPSTypeRGB16);
590 else
591 (void) WriteBlobLong(image, VIPSTypeRGB);
592 break;
593 case XYZColorspace:
594 (void) WriteBlobLong(image,VIPSTypeXYZ);
595 break;
596 default:
597 case sRGBColorspace:
598 (void) SetImageColorspace(image,sRGBColorspace,exception);
599 (void) WriteBlobLong(image,VIPSTypesRGB);
600 break;
601 }
602 if (image->units == PixelsPerCentimeterResolution)
603 {
604 (void) WriteBlobFloat(image,(image->resolution.x / 10));
605 (void) WriteBlobFloat(image,(image->resolution.y / 10));
606 }
607 else if (image->units == PixelsPerInchResolution)
608 {
609 (void) WriteBlobFloat(image,(image->resolution.x / 25.4));
610 (void) WriteBlobFloat(image,(image->resolution.y / 25.4));
611 }
612 else
613 {
614 (void) WriteBlobLong(image,0);
615 (void) WriteBlobLong(image,0);
616 }
617 /*
618 Legacy, Offsets, Future
619 */
620 for (y=0; y < 24; y++)
621 (void) WriteBlobByte(image,0);
622 for (y=0; y < (ssize_t) image->rows; y++)
623 {
624 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
625 if (p == (const Quantum *) NULL)
626 break;
627 for (x=0; x < (ssize_t) image->columns; x++)
628 {
629 WriteVIPSPixel(image,GetPixelRed(image,p));
630 if (channels == 2)
631 WriteVIPSPixel(image,GetPixelAlpha(image,p));
632 else
633 {
634 WriteVIPSPixel(image,GetPixelGreen(image,p));
635 WriteVIPSPixel(image,GetPixelBlue(image,p));
636 if (channels >= 4)
637 {
638 if (image->colorspace == CMYKColorspace)
639 WriteVIPSPixel(image,GetPixelIndex(image,p));
640 else
641 WriteVIPSPixel(image,GetPixelAlpha(image,p));
642 }
643 else if (channels == 5)
644 {
645 WriteVIPSPixel(image,GetPixelIndex(image,p));
646 WriteVIPSPixel(image,GetPixelAlpha(image,p));
647 }
648 }
649 p+=GetPixelChannels(image);
650 }
651 }
652 metadata=GetImageProperty(image,"vips:metadata",exception);
653 if (metadata != (const char*) NULL)
654 WriteBlobString(image,metadata);
655 (void) CloseBlob(image);
656 return(status);
657}