You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

759 lines
25 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% V V IIIII PPPP SSSSS %
% V V I P P SS %
% V V I PPPP SSS %
% V V I P SS %
% V IIIII P SSSSS %
% %
% %
% Read/Write VIPS Image Format %
% %
% Software Design %
% Dirk Lemstra %
% April 2014 %
% %
% %
% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
% obtain a copy of the License at %
% %
% https://imagemagick.org/script/license.php %
% %
% Unless required by applicable law or agreed to in writing, software %
% distributed under the License is distributed on an "AS IS" BASIS, %
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
% See the License for the specific language governing permissions and %
% limitations under the License. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/
/*
Include declarations.
*/
#include "MagickCore/studio.h"
#include "MagickCore/attribute.h"
#include "MagickCore/blob.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache.h"
#include "MagickCore/colorspace.h"
#include "MagickCore/colorspace-private.h"
#include "MagickCore/exception.h"
#include "MagickCore/exception-private.h"
#include "MagickCore/image.h"
#include "MagickCore/image-private.h"
#include "MagickCore/list.h"
#include "MagickCore/magick.h"
#include "MagickCore/memory_.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/property.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/static.h"
#include "MagickCore/string_.h"
#include "MagickCore/module.h"
/*
Define declaractions.
*/
#define VIPS_MAGIC_LSB 0x08f2a6b6U
#define VIPS_MAGIC_MSB 0xb6a6f208U
typedef enum
{
VIPSBandFormatNOTSET = -1,
VIPSBandFormatUCHAR = 0, /* Unsigned 8-bit int */
VIPSBandFormatCHAR = 1, /* Signed 8-bit int */
VIPSBandFormatUSHORT = 2, /* Unsigned 16-bit int */
VIPSBandFormatSHORT = 3, /* Signed 16-bit int */
VIPSBandFormatUINT = 4, /* Unsigned 32-bit int */
VIPSBandFormatINT = 5, /* Signed 32-bit int */
VIPSBandFormatFLOAT = 6, /* 32-bit IEEE float */
VIPSBandFormatCOMPLEX = 7, /* Complex (2 floats) */
VIPSBandFormatDOUBLE = 8, /* 64-bit IEEE double */
VIPSBandFormatDPCOMPLEX = 9 /* Complex (2 doubles) */
} VIPSBandFormat;
typedef enum
{
VIPSCodingNONE = 0, /* VIPS computation format */
VIPSCodingLABQ = 2, /* LABQ storage format */
VIPSCodingRAD = 6 /* Radiance storage format */
} VIPSCoding;
typedef enum
{
VIPSTypeMULTIBAND = 0, /* Some multiband image */
VIPSTypeB_W = 1, /* Some single band image */
VIPSTypeHISTOGRAM = 10, /* Histogram or LUT */
VIPSTypeFOURIER = 24, /* Image in Fourier space */
VIPSTypeXYZ = 12, /* CIE XYZ color space */
VIPSTypeLAB = 13, /* CIE LAB color space */
VIPSTypeCMYK = 15, /* im_icc_export() */
VIPSTypeLABQ = 16, /* 32-bit CIE LAB */
VIPSTypeRGB = 17, /* Some RGB */
VIPSTypeUCS = 18, /* UCS(1:1) color space */
VIPSTypeLCH = 19, /* CIE LCh color space */
VIPSTypeLABS = 21, /* 48-bit CIE LAB */
VIPSTypesRGB = 22, /* sRGB color space */
VIPSTypeYXY = 23, /* CIE Yxy color space */
VIPSTypeRGB16 = 25, /* 16-bit RGB */
VIPSTypeGREY16 = 26 /* 16-bit monochrome */
} VIPSType;
/*
Forward declarations.
*/
static MagickBooleanType
WriteVIPSImage(const ImageInfo *,Image *,ExceptionInfo *);
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s V I P S %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% IsVIPS() returns MagickTrue if the image format type, identified by the
% magick string, is VIPS.
%
% The format of the IsVIPS method is:
%
% MagickBooleanType IsVIPS(const unsigned char *magick,const size_t length)
%
% A description of each parameter follows:
%
% o magick: compare image format pattern against these bytes.
%
% o length: Specifies the length of the magick string.
%
*/
static MagickBooleanType IsVIPS(const unsigned char *magick,const size_t length)
{
if (length < 4)
return(MagickFalse);
if (memcmp(magick,"\010\362\246\266",4) == 0)
return(MagickTrue);
if (memcmp(magick,"\266\246\362\010",4) == 0)
return(MagickTrue);
return(MagickFalse);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d V I P S I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ReadVIPSImage() reads a VIPS image file and returns it. It allocates the
% memory necessary for the new Image structure and returns a pointer to the
% new image.
%
% The format of the ReadVIPSImage method is:
%
% Image *ReadVIPSmage(const ImageInfo *image_info,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image_info: the image info.
%
% o exception: return any errors or warnings in this structure.
%
*/
static inline MagickBooleanType IsSupportedCombination(
const VIPSBandFormat format,const VIPSType type)
{
switch(type)
{
case VIPSTypeB_W:
case VIPSTypeCMYK:
case VIPSTypeRGB:
case VIPSTypesRGB:
return(MagickTrue);
case VIPSTypeGREY16:
case VIPSTypeRGB16:
switch(format)
{
case VIPSBandFormatUSHORT:
case VIPSBandFormatSHORT:
case VIPSBandFormatUINT:
case VIPSBandFormatINT:
case VIPSBandFormatFLOAT:
case VIPSBandFormatDOUBLE:
return(MagickTrue);
default:
return(MagickFalse);
}
default:
return(MagickFalse);
}
}
static inline Quantum ReadVIPSPixelNONE(Image *image,
const VIPSBandFormat format,const VIPSType type)
{
switch(type)
{
case VIPSTypeB_W:
case VIPSTypeRGB:
{
unsigned char
c;
switch(format)
{
case VIPSBandFormatUCHAR:
case VIPSBandFormatCHAR:
c=(unsigned char) ReadBlobByte(image);
break;
case VIPSBandFormatUSHORT:
case VIPSBandFormatSHORT:
c=(unsigned char) ReadBlobShort(image);
break;
case VIPSBandFormatUINT:
case VIPSBandFormatINT:
c=(unsigned char) ReadBlobLong(image);
break;
case VIPSBandFormatFLOAT:
c=(unsigned char) ReadBlobFloat(image);
break;
case VIPSBandFormatDOUBLE:
c=(unsigned char) ReadBlobDouble(image);
break;
default:
c=0;
break;
}
return(ScaleCharToQuantum(c));
}
case VIPSTypeGREY16:
case VIPSTypeRGB16:
{
unsigned short
s;
switch(format)
{
case VIPSBandFormatUSHORT:
case VIPSBandFormatSHORT:
s=(unsigned short) ReadBlobShort(image);
break;
case VIPSBandFormatUINT:
case VIPSBandFormatINT:
s=(unsigned short) ReadBlobLong(image);
break;
case VIPSBandFormatFLOAT:
s=(unsigned short) ReadBlobFloat(image);
break;
case VIPSBandFormatDOUBLE:
s=(unsigned short) ReadBlobDouble(image);
break;
default:
s=0;
break;
}
return(ScaleShortToQuantum(s));
}
case VIPSTypeCMYK:
case VIPSTypesRGB:
switch(format)
{
case VIPSBandFormatUCHAR:
case VIPSBandFormatCHAR:
return(ScaleCharToQuantum((unsigned char) ReadBlobByte(image)));
case VIPSBandFormatUSHORT:
case VIPSBandFormatSHORT:
return(ScaleShortToQuantum(ReadBlobShort(image)));
case VIPSBandFormatUINT:
case VIPSBandFormatINT:
return(ScaleLongToQuantum(ReadBlobLong(image)));
case VIPSBandFormatFLOAT:
return((Quantum) ((float) QuantumRange*(ReadBlobFloat(image)/1.0)));
case VIPSBandFormatDOUBLE:
return((Quantum) ((double) QuantumRange*(ReadBlobDouble(
image)/1.0)));
default:
return((Quantum) 0);
}
default:
return((Quantum) 0);
}
}
static MagickBooleanType ReadVIPSPixelsNONE(Image *image,
const VIPSBandFormat format,const VIPSType type,const unsigned int channels,
ExceptionInfo *exception)
{
Quantum
pixel;
Quantum
*q;
ssize_t
x;
ssize_t
y;
for (y = 0; y < (ssize_t) image->rows; y++)
{
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL)
return MagickFalse;
for (x=0; x < (ssize_t) image->columns; x++)
{
pixel=ReadVIPSPixelNONE(image,format,type);
SetPixelRed(image,pixel,q);
if (channels < 3)
{
SetPixelGreen(image,pixel,q);
SetPixelBlue(image,pixel,q);
if (channels == 2)
SetPixelAlpha(image,ReadVIPSPixelNONE(image,format,type),q);
}
else
{
SetPixelGreen(image,ReadVIPSPixelNONE(image,format,type),q);
SetPixelBlue(image,ReadVIPSPixelNONE(image,format,type),q);
if (channels == 4)
{
if (image->colorspace == CMYKColorspace)
SetPixelIndex(image,ReadVIPSPixelNONE(image,format,type),q);
else
SetPixelAlpha(image,ReadVIPSPixelNONE(image,format,type),q);
}
else if (channels == 5)
{
SetPixelIndex(image,ReadVIPSPixelNONE(image,format,type),q);
SetPixelAlpha(image,ReadVIPSPixelNONE(image,format,type),q);
}
}
q+=GetPixelChannels(image);
}
if (SyncAuthenticPixels(image,exception) == MagickFalse)
return MagickFalse;
}
return(MagickTrue);
}
static Image *ReadVIPSImage(const ImageInfo *image_info,
ExceptionInfo *exception)
{
char
buffer[MagickPathExtent],
*metadata;
Image
*image;
MagickBooleanType
status;
ssize_t
n;
unsigned int
channels,
marker;
VIPSBandFormat
format;
VIPSCoding
coding;
VIPSType
type;
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
if (image_info->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
image_info->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickCoreSignature);
image=AcquireImage(image_info,exception);
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
if (status == MagickFalse)
{
image=DestroyImageList(image);
return((Image *) NULL);
}
marker=ReadBlobLSBLong(image);
if (marker == VIPS_MAGIC_LSB)
image->endian=LSBEndian;
else if (marker == VIPS_MAGIC_MSB)
image->endian=MSBEndian;
else
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
image->columns=(size_t) ReadBlobLong(image);
image->rows=(size_t) ReadBlobLong(image);
status=SetImageExtent(image,image->columns,image->rows,exception);
if (status == MagickFalse)
return(DestroyImageList(image));
channels=ReadBlobLong(image);
(void) ReadBlobLong(image); /* Legacy */
format=(VIPSBandFormat) ReadBlobLong(image);
switch(format)
{
case VIPSBandFormatUCHAR:
case VIPSBandFormatCHAR:
image->depth=8;
break;
case VIPSBandFormatUSHORT:
case VIPSBandFormatSHORT:
image->depth=16;
break;
case VIPSBandFormatUINT:
case VIPSBandFormatINT:
case VIPSBandFormatFLOAT:
image->depth=32;
break;
case VIPSBandFormatDOUBLE:
image->depth=64;
break;
default:
case VIPSBandFormatCOMPLEX:
case VIPSBandFormatDPCOMPLEX:
case VIPSBandFormatNOTSET:
ThrowReaderException(CoderError,"Unsupported band format");
}
coding=(VIPSCoding) ReadBlobLong(image);
type=(VIPSType) ReadBlobLong(image);
switch(type)
{
case VIPSTypeCMYK:
SetImageColorspace(image,CMYKColorspace,exception);
if (channels == 5)
image->alpha_trait=BlendPixelTrait;
break;
case VIPSTypeB_W:
case VIPSTypeGREY16:
SetImageColorspace(image,GRAYColorspace,exception);
if (channels == 2)
image->alpha_trait=BlendPixelTrait;
break;
case VIPSTypeRGB:
case VIPSTypeRGB16:
SetImageColorspace(image,RGBColorspace,exception);
if (channels == 4)
image->alpha_trait=BlendPixelTrait;
break;
case VIPSTypesRGB:
SetImageColorspace(image,sRGBColorspace,exception);
if (channels == 4)
image->alpha_trait=BlendPixelTrait;
break;
default:
case VIPSTypeFOURIER:
case VIPSTypeHISTOGRAM:
case VIPSTypeLAB:
case VIPSTypeLABS:
case VIPSTypeLABQ:
case VIPSTypeLCH:
case VIPSTypeMULTIBAND:
case VIPSTypeUCS:
case VIPSTypeXYZ:
case VIPSTypeYXY:
ThrowReaderException(CoderError,"Unsupported colorspace");
}
(void) SetImageBackgroundColor(image,exception);
image->units=PixelsPerCentimeterResolution;
image->resolution.x=ReadBlobFloat(image)*10;
image->resolution.y=ReadBlobFloat(image)*10;
/*
Legacy, offsets, future
*/
(void) ReadBlobLongLong(image);
(void) ReadBlobLongLong(image);
(void) ReadBlobLongLong(image);
if (image_info->ping != MagickFalse)
return(image);
if (IsSupportedCombination(format,type) == MagickFalse)
ThrowReaderException(CoderError,
"Unsupported combination of band format and colorspace");
if (channels == 0 || channels > 5)
ThrowReaderException(CoderError,"Unsupported number of channels");
if (coding == VIPSCodingNONE)
status=ReadVIPSPixelsNONE(image,format,type,channels,exception);
else
ThrowReaderException(CoderError,"Unsupported coding");
metadata=(char *) NULL;
while ((n=ReadBlob(image,MagickPathExtent-1,(unsigned char *) buffer)) != 0)
{
buffer[n]='\0';
if (metadata == (char *) NULL)
metadata=ConstantString(buffer);
else
(void) ConcatenateString(&metadata,buffer);
}
if (metadata != (char *) NULL)
{
SetImageProperty(image,"vips:metadata",metadata,exception);
metadata=(char *) RelinquishMagickMemory(metadata);
}
(void) CloseBlob(image);
if (status == MagickFalse)
return((Image *) NULL);
return(image);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e g i s t e r V I P S I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% RegisterVIPSmage() adds attributes for the VIPS image format to the list
% of supported formats. The attributes include the image format tag, a
% method to read and/or write the format, whether the format supports the
% saving of more than one frame to the same file or blob, whether the format
% supports native in-memory I/O, and a brief description of the format.
%
% The format of the RegisterVIPSImage method is:
%
% size_t RegisterVIPSImage(void)
%
*/
ModuleExport size_t RegisterVIPSImage(void)
{
MagickInfo
*entry;
entry=AcquireMagickInfo("VIPS","VIPS","VIPS image");
entry->decoder=(DecodeImageHandler *) ReadVIPSImage;
entry->encoder=(EncodeImageHandler *) WriteVIPSImage;
entry->magick=(IsImageFormatHandler *) IsVIPS;
entry->flags|=CoderEndianSupportFlag;
(void) RegisterMagickInfo(entry);
return(MagickImageCoderSignature);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% U n r e g i s t e r V I P S I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% UnregisterVIPSImage() removes format registrations made by the
% VIPS module from the list of supported formats.
%
% The format of the UnregisterVIPSImage method is:
%
% UnregisterVIPSImage(void)
%
*/
ModuleExport void UnregisterVIPSImage(void)
{
(void) UnregisterMagickInfo("VIPS");
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% W r i t e V I P S I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% WriteVIPSImage() writes an image to a file in VIPS image format.
%
% The format of the WriteVIPSImage method is:
%
% MagickBooleanType WriteVIPSImage(const ImageInfo *image_info,Image *image)
%
% A description of each parameter follows.
%
% o image_info: the image info.
%
% o image: The image.
%
*/
static inline void WriteVIPSPixel(Image *image, const Quantum value)
{
if (image->depth == 16)
(void) WriteBlobShort(image,ScaleQuantumToShort(value));
else
(void) WriteBlobByte(image,ScaleQuantumToChar(value));
}
static MagickBooleanType WriteVIPSImage(const ImageInfo *image_info,
Image *image,ExceptionInfo *exception)
{
const char
*metadata;
MagickBooleanType
status;
const Quantum
*p;
ssize_t
x;
ssize_t
y;
unsigned int
channels;
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
if (status == MagickFalse)
return(status);
if (image->endian == LSBEndian)
(void) WriteBlobLSBLong(image,VIPS_MAGIC_LSB);
else
(void) WriteBlobLSBLong(image,VIPS_MAGIC_MSB);
(void) WriteBlobLong(image,(unsigned int) image->columns);
(void) WriteBlobLong(image,(unsigned int) image->rows);
(void) SetImageStorageClass(image,DirectClass,exception);
channels=image->alpha_trait != UndefinedPixelTrait ? 4 : 3;
if (SetImageGray(image,exception) != MagickFalse)
channels=image->alpha_trait != UndefinedPixelTrait ? 2 : 1;
else if (image->colorspace == CMYKColorspace)
channels=image->alpha_trait != UndefinedPixelTrait ? 5 : 4;
(void) WriteBlobLong(image,channels);
(void) WriteBlobLong(image,0);
if (image->depth == 16)
(void) WriteBlobLong(image,(unsigned int) VIPSBandFormatUSHORT);
else
{
image->depth=8;
(void) WriteBlobLong(image,(unsigned int) VIPSBandFormatUCHAR);
}
(void) WriteBlobLong(image,VIPSCodingNONE);
switch(image->colorspace)
{
case CMYKColorspace:
(void) WriteBlobLong(image,VIPSTypeCMYK);
break;
case GRAYColorspace:
if (image->depth == 16)
(void) WriteBlobLong(image, VIPSTypeGREY16);
else
(void) WriteBlobLong(image, VIPSTypeB_W);
break;
case LabColorspace:
(void) WriteBlobLong(image,VIPSTypeLAB);
break;
case LCHColorspace:
(void) WriteBlobLong(image,VIPSTypeLCH);
break;
case RGBColorspace:
if (image->depth == 16)
(void) WriteBlobLong(image, VIPSTypeRGB16);
else
(void) WriteBlobLong(image, VIPSTypeRGB);
break;
case XYZColorspace:
(void) WriteBlobLong(image,VIPSTypeXYZ);
break;
default:
case sRGBColorspace:
(void) SetImageColorspace(image,sRGBColorspace,exception);
(void) WriteBlobLong(image,VIPSTypesRGB);
break;
}
if (image->units == PixelsPerCentimeterResolution)
{
(void) WriteBlobFloat(image,(image->resolution.x / 10));
(void) WriteBlobFloat(image,(image->resolution.y / 10));
}
else if (image->units == PixelsPerInchResolution)
{
(void) WriteBlobFloat(image,(image->resolution.x / 25.4));
(void) WriteBlobFloat(image,(image->resolution.y / 25.4));
}
else
{
(void) WriteBlobLong(image,0);
(void) WriteBlobLong(image,0);
}
/*
Legacy, Offsets, Future
*/
for (y=0; y < 24; y++)
(void) WriteBlobByte(image,0);
for (y=0; y < (ssize_t) image->rows; y++)
{
p=GetVirtualPixels(image,0,y,image->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
for (x=0; x < (ssize_t) image->columns; x++)
{
WriteVIPSPixel(image,GetPixelRed(image,p));
if (channels == 2)
WriteVIPSPixel(image,GetPixelAlpha(image,p));
else
{
WriteVIPSPixel(image,GetPixelGreen(image,p));
WriteVIPSPixel(image,GetPixelBlue(image,p));
if (channels >= 4)
{
if (image->colorspace == CMYKColorspace)
WriteVIPSPixel(image,GetPixelIndex(image,p));
else
WriteVIPSPixel(image,GetPixelAlpha(image,p));
}
else if (channels == 5)
{
WriteVIPSPixel(image,GetPixelIndex(image,p));
WriteVIPSPixel(image,GetPixelAlpha(image,p));
}
}
p+=GetPixelChannels(image);
}
}
metadata=GetImageProperty(image,"vips:metadata",exception);
if (metadata != (const char*) NULL)
WriteBlobString(image,metadata);
(void) CloseBlob(image);
return(status);
}