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.

1389 lines
48 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.

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% IIIII CCCC OOO N N %
% I C O O NN N %
% I C O O N N N %
% I C O O N NN %
% IIIII CCCC OOO N N %
% %
% %
% Read Microsoft Windows Icon Format %
% %
% Software Design %
% Cristy %
% July 1992 %
% %
% %
% 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/artifact.h"
#include "MagickCore/blob.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache.h"
#include "MagickCore/colormap.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/log.h"
#include "MagickCore/magick.h"
#include "MagickCore/memory_.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/nt-base-private.h"
#include "MagickCore/option.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/quantize.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/static.h"
#include "MagickCore/string_.h"
#include "MagickCore/module.h"
/*
Define declarations.
*/
#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__MINGW32__)
#define BI_RGB 0
#define BI_RLE8 1
#define BI_BITFIELDS 3
#endif
#define MaxIcons 1024
/*
Typedef declarations.
*/
typedef struct _IconEntry
{
unsigned char
width,
height,
colors,
reserved;
unsigned short int
planes,
bits_per_pixel;
size_t
size,
offset;
} IconEntry;
typedef struct _IconFile
{
short
reserved,
resource_type,
count;
IconEntry
directory[MaxIcons];
} IconFile;
typedef struct _IconInfo
{
size_t
file_size,
ba_offset,
offset_bits,
size;
ssize_t
width,
height;
unsigned short
planes,
bits_per_pixel;
size_t
compression,
image_size,
x_pixels,
y_pixels,
number_colors,
red_mask,
green_mask,
blue_mask,
alpha_mask,
colors_important;
ssize_t
colorspace;
} IconInfo;
/*
Forward declaractions.
*/
static MagickBooleanType
WriteICONImage(const ImageInfo *,Image *,ExceptionInfo *);
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d I C O N I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ReadICONImage() reads a Microsoft icon 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 ReadICONImage method is:
%
% Image *ReadICONImage(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 Image *ReadICONImage(const ImageInfo *image_info,
ExceptionInfo *exception)
{
IconFile
icon_file;
IconInfo
icon_info;
Image
*image;
MagickBooleanType
status;
MagickSizeType
extent;
ssize_t
i,
x;
Quantum
*q;
unsigned char
*p;
size_t
bit,
byte,
bytes_per_line,
one,
scanline_pad;
ssize_t
count,
offset,
y;
/*
Open image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
(void) LogMagickEvent(CoderEvent,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);
}
icon_file.reserved=(short) ReadBlobLSBShort(image);
icon_file.resource_type=(short) ReadBlobLSBShort(image);
icon_file.count=(short) ReadBlobLSBShort(image);
if ((icon_file.reserved != 0) ||
((icon_file.resource_type != 1) && (icon_file.resource_type != 2)) ||
(icon_file.count > MaxIcons))
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
extent=0;
for (i=0; i < icon_file.count; i++)
{
icon_file.directory[i].width=(unsigned char) ReadBlobByte(image);
icon_file.directory[i].height=(unsigned char) ReadBlobByte(image);
icon_file.directory[i].colors=(unsigned char) ReadBlobByte(image);
icon_file.directory[i].reserved=(unsigned char) ReadBlobByte(image);
icon_file.directory[i].planes=(unsigned short) ReadBlobLSBShort(image);
icon_file.directory[i].bits_per_pixel=(unsigned short)
ReadBlobLSBShort(image);
icon_file.directory[i].size=ReadBlobLSBLong(image);
icon_file.directory[i].offset=ReadBlobLSBLong(image);
if (EOFBlob(image) != MagickFalse)
break;
extent=MagickMax(extent,icon_file.directory[i].size);
}
if ((EOFBlob(image) != MagickFalse) || (extent > GetBlobSize(image)))
ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
one=1;
for (i=0; i < icon_file.count; i++)
{
/*
Verify Icon identifier.
*/
offset=(ssize_t) SeekBlob(image,(MagickOffsetType)
icon_file.directory[i].offset,SEEK_SET);
if (offset < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
icon_info.size=ReadBlobLSBLong(image);
icon_info.width=(unsigned char) ReadBlobLSBSignedLong(image);
icon_info.height=(unsigned char) (ReadBlobLSBSignedLong(image)/2);
icon_info.planes=ReadBlobLSBShort(image);
icon_info.bits_per_pixel=ReadBlobLSBShort(image);
if (EOFBlob(image) != MagickFalse)
{
ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
image->filename);
break;
}
if (((icon_info.planes == 18505) && (icon_info.bits_per_pixel == 21060)) ||
(icon_info.size == 0x474e5089))
{
Image
*icon_image;
ImageInfo
*read_info;
size_t
length;
unsigned char
*png;
/*
Icon image encoded as a compressed PNG image.
*/
length=icon_file.directory[i].size;
if ((length < 16) || (~length < 16))
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
png=(unsigned char *) AcquireQuantumMemory(length,sizeof(*png));
if (png == (unsigned char *) NULL)
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
(void) memcpy(png,"\211PNG\r\n\032\n\000\000\000\015",12);
png[12]=(unsigned char) icon_info.planes;
png[13]=(unsigned char) (icon_info.planes >> 8);
png[14]=(unsigned char) icon_info.bits_per_pixel;
png[15]=(unsigned char) (icon_info.bits_per_pixel >> 8);
count=ReadBlob(image,length-16,png+16);
if (count != (ssize_t) (length-16))
{
png=(unsigned char *) RelinquishMagickMemory(png);
ThrowReaderException(CorruptImageError,
"InsufficientImageDataInFile");
}
read_info=CloneImageInfo(image_info);
(void) CopyMagickString(read_info->magick,"PNG",MagickPathExtent);
icon_image=BlobToImage(read_info,png,length,exception);
read_info=DestroyImageInfo(read_info);
png=(unsigned char *) RelinquishMagickMemory(png);
if (icon_image == (Image *) NULL)
return(DestroyImageList(image));
DestroyBlob(icon_image);
icon_image->blob=ReferenceBlob(image->blob);
ReplaceImageInList(&image,icon_image);
}
else
{
if (icon_info.bits_per_pixel > 32)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
icon_info.compression=ReadBlobLSBLong(image);
icon_info.image_size=ReadBlobLSBLong(image);
icon_info.x_pixels=ReadBlobLSBLong(image);
icon_info.y_pixels=ReadBlobLSBLong(image);
icon_info.number_colors=ReadBlobLSBLong(image);
if (icon_info.number_colors > GetBlobSize(image))
ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
icon_info.colors_important=ReadBlobLSBLong(image);
image->alpha_trait=BlendPixelTrait;
image->columns=(size_t) icon_file.directory[i].width;
if ((ssize_t) image->columns > icon_info.width)
image->columns=(size_t) icon_info.width;
if (image->columns == 0)
image->columns=256;
image->rows=(size_t) icon_file.directory[i].height;
if ((ssize_t) image->rows > icon_info.height)
image->rows=(size_t) icon_info.height;
if (image->rows == 0)
image->rows=256;
image->depth=icon_info.bits_per_pixel;
if (image->depth > 16)
image->depth=8;
if (image->debug != MagickFalse)
{
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" scene = %.20g",(double) i);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" size = %.20g",(double) icon_info.size);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" width = %.20g",(double) icon_file.directory[i].width);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" height = %.20g",(double) icon_file.directory[i].height);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" colors = %.20g",(double ) icon_info.number_colors);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" planes = %.20g",(double) icon_info.planes);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),
" bpp = %.20g",(double) icon_info.bits_per_pixel);
}
if ((icon_info.number_colors != 0) || (icon_info.bits_per_pixel <= 16U))
{
image->storage_class=PseudoClass;
image->colors=icon_info.number_colors;
if ((image->colors == 0) || (image->colors > 256))
image->colors=one << icon_info.bits_per_pixel;
}
if (image->storage_class == PseudoClass)
{
ssize_t
j;
unsigned char
*icon_colormap;
/*
Read Icon raster colormap.
*/
if (image->colors > GetBlobSize(image))
ThrowReaderException(CorruptImageError,
"InsufficientImageDataInFile");
if (AcquireImageColormap(image,image->colors,exception) ==
MagickFalse)
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
image->colors,4UL*sizeof(*icon_colormap));
if (icon_colormap == (unsigned char *) NULL)
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
count=ReadBlob(image,(size_t) (4*image->colors),icon_colormap);
if (count != (ssize_t) (4*image->colors))
{
icon_colormap=(unsigned char *) RelinquishMagickMemory(
icon_colormap);
ThrowReaderException(CorruptImageError,
"InsufficientImageDataInFile");
}
p=icon_colormap;
for (j=0; j < (ssize_t) image->colors; j++)
{
image->colormap[j].blue=(Quantum) ScaleCharToQuantum(*p++);
image->colormap[j].green=(Quantum) ScaleCharToQuantum(*p++);
image->colormap[j].red=(Quantum) ScaleCharToQuantum(*p++);
p++;
}
icon_colormap=(unsigned char *) RelinquishMagickMemory(icon_colormap);
}
/*
Convert Icon raster image to pixel packets.
*/
if ((image_info->ping != MagickFalse) &&
(image_info->number_scenes != 0))
if (image->scene >= (image_info->scene+image_info->number_scenes-1))
break;
status=SetImageExtent(image,image->columns,image->rows,exception);
if (status == MagickFalse)
return(DestroyImageList(image));
bytes_per_line=(((image->columns*icon_info.bits_per_pixel)+31) &
~31) >> 3;
(void) bytes_per_line;
scanline_pad=((((image->columns*icon_info.bits_per_pixel)+31) & ~31)-
(image->columns*icon_info.bits_per_pixel)) >> 3;
switch (icon_info.bits_per_pixel)
{
case 1:
{
/*
Convert bitmap scanline.
*/
for (y=(ssize_t) image->rows-1; y >= 0; y--)
{
q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) (image->columns-7); x+=8)
{
byte=(size_t) ReadBlobByte(image);
for (bit=0; bit < 8; bit++)
{
SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
0x00),q);
q+=GetPixelChannels(image);
}
}
if ((image->columns % 8) != 0)
{
byte=(size_t) ReadBlobByte(image);
for (bit=0; bit < (image->columns % 8); bit++)
{
SetPixelIndex(image,((byte & (0x80 >> bit)) != 0 ? 0x01 :
0x00),q);
q+=GetPixelChannels(image);
}
}
for (x=0; x < (ssize_t) scanline_pad; x++)
(void) ReadBlobByte(image);
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
if (image->previous == (Image *) NULL)
{
status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
image->rows);
if (status == MagickFalse)
break;
}
}
break;
}
case 4:
{
/*
Read 4-bit Icon scanline.
*/
for (y=(ssize_t) image->rows-1; y >= 0; y--)
{
q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < ((ssize_t) image->columns-1); x+=2)
{
byte=(size_t) ReadBlobByte(image);
SetPixelIndex(image,((byte >> 4) & 0xf),q);
q+=GetPixelChannels(image);
SetPixelIndex(image,((byte) & 0xf),q);
q+=GetPixelChannels(image);
}
if ((image->columns % 2) != 0)
{
byte=(size_t) ReadBlobByte(image);
SetPixelIndex(image,((byte >> 4) & 0xf),q);
q+=GetPixelChannels(image);
}
for (x=0; x < (ssize_t) scanline_pad; x++)
(void) ReadBlobByte(image);
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
if (image->previous == (Image *) NULL)
{
status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
image->rows);
if (status == MagickFalse)
break;
}
}
break;
}
case 8:
{
/*
Convert PseudoColor scanline.
*/
for (y=(ssize_t) image->rows-1; y >= 0; y--)
{
q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) image->columns; x++)
{
byte=(size_t) ReadBlobByte(image);
SetPixelIndex(image,(Quantum) byte,q);
q+=GetPixelChannels(image);
}
for (x=0; x < (ssize_t) scanline_pad; x++)
(void) ReadBlobByte(image);
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
if (image->previous == (Image *) NULL)
{
status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
image->rows);
if (status == MagickFalse)
break;
}
}
break;
}
case 16:
{
/*
Convert PseudoColor scanline.
*/
for (y=(ssize_t) image->rows-1; y >= 0; y--)
{
q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) image->columns; x++)
{
byte=(size_t) ReadBlobByte(image);
byte|=((size_t) ReadBlobByte(image) << 8);
SetPixelIndex(image,(Quantum) byte,q);
q+=GetPixelChannels(image);
}
for (x=0; x < (ssize_t) scanline_pad; x++)
(void) ReadBlobByte(image);
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
if (image->previous == (Image *) NULL)
{
status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
image->rows);
if (status == MagickFalse)
break;
}
}
break;
}
case 24:
case 32:
{
/*
Convert DirectColor scanline.
*/
for (y=(ssize_t) image->rows-1; y >= 0; y--)
{
q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) image->columns; x++)
{
SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
ReadBlobByte(image)),q);
SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
ReadBlobByte(image)),q);
SetPixelRed(image,ScaleCharToQuantum((unsigned char)
ReadBlobByte(image)),q);
if (icon_info.bits_per_pixel == 32)
SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
ReadBlobByte(image)),q);
q+=GetPixelChannels(image);
}
if (icon_info.bits_per_pixel == 24)
for (x=0; x < (ssize_t) scanline_pad; x++)
(void) ReadBlobByte(image);
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
if (image->previous == (Image *) NULL)
{
status=SetImageProgress(image,LoadImageTag,image->rows-y-1,
image->rows);
if (status == MagickFalse)
break;
}
}
break;
}
default:
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
}
if ((image_info->ping == MagickFalse) &&
(icon_info.bits_per_pixel <= 16))
(void) SyncImage(image,exception);
if (icon_info.bits_per_pixel != 32)
{
/*
Read the ICON alpha mask.
*/
image->storage_class=DirectClass;
for (y=(ssize_t) image->rows-1; y >= 0; y--)
{
q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < ((ssize_t) image->columns-7); x+=8)
{
byte=(size_t) ReadBlobByte(image);
for (bit=0; bit < 8; bit++)
{
SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
TransparentAlpha : OpaqueAlpha),q);
q+=GetPixelChannels(image);
}
}
if ((image->columns % 8) != 0)
{
byte=(size_t) ReadBlobByte(image);
for (bit=0; bit < (image->columns % 8); bit++)
{
SetPixelAlpha(image,(((byte & (0x80 >> bit)) != 0) ?
TransparentAlpha : OpaqueAlpha),q);
q+=GetPixelChannels(image);
}
}
if ((image->columns % 32) != 0)
for (x=0; x < (ssize_t) ((32-(image->columns % 32))/8); x++)
(void) ReadBlobByte(image);
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
}
}
if (EOFBlob(image) != MagickFalse)
{
ThrowFileException(exception,CorruptImageError,
"UnexpectedEndOfFile",image->filename);
break;
}
}
/*
Proceed to next image.
*/
if (image_info->number_scenes != 0)
if (image->scene >= (image_info->scene+image_info->number_scenes-1))
break;
if (i < (ssize_t) (icon_file.count-1))
{
/*
Allocate next image structure.
*/
AcquireNextImage(image_info,image,exception);
if (GetNextImageInList(image) == (Image *) NULL)
{
status=MagickFalse;
break;
}
image=SyncNextImageInList(image);
status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
GetBlobSize(image));
if (status == MagickFalse)
break;
}
}
(void) CloseBlob(image);
if (status == MagickFalse)
return(DestroyImageList(image));
return(GetFirstImageInList(image));
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e g i s t e r I C O N I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% RegisterICONImage() adds attributes for the Icon 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 RegisterICONImage method is:
%
% size_t RegisterICONImage(void)
%
*/
ModuleExport size_t RegisterICONImage(void)
{
MagickInfo
*entry;
entry=AcquireMagickInfo("ICON","CUR","Microsoft icon");
entry->decoder=(DecodeImageHandler *) ReadICONImage;
entry->encoder=(EncodeImageHandler *) WriteICONImage;
entry->flags^=CoderAdjoinFlag;
entry->flags|=CoderDecoderSeekableStreamFlag;
entry->flags|=CoderEncoderSeekableStreamFlag;
(void) RegisterMagickInfo(entry);
entry=AcquireMagickInfo("ICON","ICO","Microsoft icon");
entry->decoder=(DecodeImageHandler *) ReadICONImage;
entry->encoder=(EncodeImageHandler *) WriteICONImage;
entry->flags|=CoderDecoderSeekableStreamFlag;
entry->flags|=CoderEncoderSeekableStreamFlag;
(void) RegisterMagickInfo(entry);
entry=AcquireMagickInfo("ICON","ICON","Microsoft icon");
entry->decoder=(DecodeImageHandler *) ReadICONImage;
entry->encoder=(EncodeImageHandler *) WriteICONImage;
entry->flags^=CoderAdjoinFlag;
entry->flags|=CoderDecoderSeekableStreamFlag;
entry->flags|=CoderEncoderSeekableStreamFlag;
(void) RegisterMagickInfo(entry);
return(MagickImageCoderSignature);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% U n r e g i s t e r I C O N I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% UnregisterICONImage() removes format registrations made by the
% ICON module from the list of supported formats.
%
% The format of the UnregisterICONImage method is:
%
% UnregisterICONImage(void)
%
*/
ModuleExport void UnregisterICONImage(void)
{
(void) UnregisterMagickInfo("CUR");
(void) UnregisterMagickInfo("ICO");
(void) UnregisterMagickInfo("ICON");
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% W r i t e I C O N I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% WriteICONImage() writes an image in Microsoft Windows bitmap encoded
% image format, version 3 for Windows or (if the image has a matte channel)
% version 4.
%
% It encodes any subimage as a compressed PNG image ("BI_PNG)", only when its
% dimensions are 256x256 and image->compression is undefined or is defined as
% ZipCompression.
%
% The format of the WriteICONImage method is:
%
% MagickBooleanType WriteICONImage(const ImageInfo *image_info,
% Image *image,ExceptionInfo *exception)
%
% A description of each parameter follows.
%
% o image_info: the image info.
%
% o image: The image.
%
% o exception: return any errors or warnings in this structure.
%
*/
static Image *AutoResizeImage(const Image *image,const char *option,
MagickOffsetType *count,ExceptionInfo *exception)
{
#define MAX_SIZES 16
char
*q;
const char
*p;
Image
*images,
*resized;
ssize_t
i;
size_t
sizes[MAX_SIZES] ={ 256, 192, 128, 96, 64, 48, 40, 32, 24, 16};
images=NULL;
*count=0;
i=0;
p=option;
while ((*p != '\0') && (i < MAX_SIZES))
{
size_t
size;
while ((isspace((int) ((unsigned char) *p)) != 0))
p++;
size=(size_t) strtol(p,&q,10);
if ((p == q) || (size < 16) || (size > 256))
return((Image *) NULL);
p=q;
sizes[i++]=size;
while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == ','))
p++;
}
if (i == 0)
i=10;
*count=i;
for (i=0; i < *count; i++)
{
resized=ResizeImage(image,sizes[i],sizes[i],image->filter,exception);
if (resized == (Image *) NULL)
return(DestroyImageList(images));
if (images == (Image *) NULL)
images=resized;
else
AppendImageToList(&images,resized);
}
return(images);
}
static MagickBooleanType WriteICONImage(const ImageInfo *image_info,
Image *image,ExceptionInfo *exception)
{
const char
*option;
IconFile
icon_file;
IconInfo
icon_info;
Image
*images,
*next;
MagickBooleanType
status;
MagickOffsetType
offset,
scene;
const Quantum
*p;
ssize_t
i,
x;
unsigned char
*q;
size_t
bytes_per_line,
imageListLength,
scanline_pad;
ssize_t
y;
unsigned char
*pixels;
/*
Open output image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",image->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickCoreSignature);
status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
if (status == MagickFalse)
return(status);
images=(Image *) NULL;
option=GetImageOption(image_info,"icon:auto-resize");
if (option != (const char *) NULL)
{
images=AutoResizeImage(image,option,&scene,exception);
if (images == (Image *) NULL)
ThrowWriterException(ImageError,"InvalidDimensions");
}
else
{
scene=0;
next=image;
do
{
if ((image->columns > 256L) || (image->rows > 256L))
ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
scene++;
next=SyncNextImageInList(next);
} while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
}
/*
Dump out a ICON header template to be properly initialized later.
*/
(void) WriteBlobLSBShort(image,0);
(void) WriteBlobLSBShort(image,1);
(void) WriteBlobLSBShort(image,(unsigned char) scene);
(void) memset(&icon_file,0,sizeof(icon_file));
(void) memset(&icon_info,0,sizeof(icon_info));
scene=0;
next=(images != (Image *) NULL) ? images : image;
do
{
(void) WriteBlobByte(image,icon_file.directory[scene].width);
(void) WriteBlobByte(image,icon_file.directory[scene].height);
(void) WriteBlobByte(image,icon_file.directory[scene].colors);
(void) WriteBlobByte(image,icon_file.directory[scene].reserved);
(void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
(void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
(void) WriteBlobLSBLong(image,(unsigned int)
icon_file.directory[scene].size);
(void) WriteBlobLSBLong(image,(unsigned int)
icon_file.directory[scene].offset);
scene++;
next=SyncNextImageInList(next);
} while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
scene=0;
next=(images != (Image *) NULL) ? images : image;
imageListLength=GetImageListLength(image);
do
{
if ((next->columns > 255L) && (next->rows > 255L) &&
((next->compression == UndefinedCompression) ||
(next->compression == ZipCompression)))
{
Image
*write_image;
ImageInfo
*write_info;
size_t
length;
unsigned char
*png;
write_image=CloneImage(next,0,0,MagickTrue,exception);
if (write_image == (Image *) NULL)
{
images=DestroyImageList(images);
return(MagickFalse);
}
write_info=CloneImageInfo(image_info);
(void) CopyMagickString(write_info->magick,"PNG",MagickPathExtent);
length=0;
/*
Don't write any ancillary chunks except for gAMA.
*/
(void) SetImageArtifact(write_image,"png:include-chunk","none,gama");
/*
Only write PNG32 formatted PNG (32-bit RGBA), 8 bits per channel.
*/
(void) SetImageArtifact(write_image,"png:format","png32");
(void) SetImageArtifact(write_image,"png:color-type","6");
png=(unsigned char *) ImageToBlob(write_info,write_image,&length,
exception);
write_image=DestroyImageList(write_image);
write_info=DestroyImageInfo(write_info);
if (png == (unsigned char *) NULL)
{
images=DestroyImageList(images);
return(MagickFalse);
}
icon_file.directory[scene].width=0;
icon_file.directory[scene].height=0;
icon_file.directory[scene].colors=0;
icon_file.directory[scene].reserved=0;
icon_file.directory[scene].planes=1;
icon_file.directory[scene].bits_per_pixel=32;
icon_file.directory[scene].size=(size_t) length;
icon_file.directory[scene].offset=(size_t) TellBlob(image);
(void) WriteBlob(image,(size_t) length,png);
png=(unsigned char *) RelinquishMagickMemory(png);
}
else
{
/*
Initialize ICON raster file header.
*/
(void) TransformImageColorspace(next,sRGBColorspace,exception);
icon_info.file_size=14+12+28;
icon_info.offset_bits=icon_info.file_size;
icon_info.compression=BI_RGB;
if ((next->storage_class != DirectClass) && (next->colors > 256))
(void) SetImageStorageClass(next,DirectClass,exception);
if (next->storage_class == DirectClass)
{
/*
Full color ICON raster.
*/
icon_info.number_colors=0;
icon_info.bits_per_pixel=32;
icon_info.compression=(size_t) BI_RGB;
}
else
{
size_t
one;
/*
Colormapped ICON raster.
*/
icon_info.bits_per_pixel=8;
if (next->colors <= 16)
icon_info.bits_per_pixel=4;
if (next->colors <= 2)
icon_info.bits_per_pixel=1;
one=1;
icon_info.number_colors=one << icon_info.bits_per_pixel;
if (icon_info.number_colors < next->colors)
{
(void) SetImageStorageClass(next,DirectClass,exception);
icon_info.number_colors=0;
icon_info.bits_per_pixel=(unsigned short) 24;
icon_info.compression=(size_t) BI_RGB;
}
else
{
one=1;
icon_info.file_size+=3*(one << icon_info.bits_per_pixel);
icon_info.offset_bits+=3*(one << icon_info.bits_per_pixel);
icon_info.file_size+=(one << icon_info.bits_per_pixel);
icon_info.offset_bits+=(one << icon_info.bits_per_pixel);
}
}
bytes_per_line=(((next->columns*icon_info.bits_per_pixel)+31) & ~31) >>
3;
icon_info.ba_offset=0;
icon_info.width=(ssize_t) next->columns;
icon_info.height=(ssize_t) next->rows;
icon_info.planes=1;
icon_info.image_size=bytes_per_line*next->rows;
icon_info.size=40;
icon_info.size+=(4*icon_info.number_colors);
icon_info.size+=icon_info.image_size;
icon_info.size+=(((icon_info.width+31) & ~31) >> 3)*icon_info.height;
icon_info.file_size+=icon_info.image_size;
icon_info.x_pixels=0;
icon_info.y_pixels=0;
switch (next->units)
{
case UndefinedResolution:
case PixelsPerInchResolution:
{
icon_info.x_pixels=(size_t) (100.0*next->resolution.x/2.54);
icon_info.y_pixels=(size_t) (100.0*next->resolution.y/2.54);
break;
}
case PixelsPerCentimeterResolution:
{
icon_info.x_pixels=(size_t) (100.0*next->resolution.x);
icon_info.y_pixels=(size_t) (100.0*next->resolution.y);
break;
}
}
icon_info.colors_important=icon_info.number_colors;
/*
Convert MIFF to ICON raster pixels.
*/
pixels=(unsigned char *) AcquireQuantumMemory((size_t)
icon_info.image_size,sizeof(*pixels));
if (pixels == (unsigned char *) NULL)
{
images=DestroyImageList(images);
ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
}
(void) memset(pixels,0,(size_t) icon_info.image_size);
switch (icon_info.bits_per_pixel)
{
case 1:
{
size_t
bit,
byte;
/*
Convert PseudoClass image to a ICON monochrome image.
*/
for (y=0; y < (ssize_t) next->rows; y++)
{
p=GetVirtualPixels(next,0,y,next->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
q=pixels+(next->rows-y-1)*bytes_per_line;
bit=0;
byte=0;
for (x=0; x < (ssize_t) next->columns; x++)
{
byte<<=1;
byte|=GetPixelIndex(next,p) != 0 ? 0x01 : 0x00;
bit++;
if (bit == 8)
{
*q++=(unsigned char) byte;
bit=0;
byte=0;
}
p+=GetPixelChannels(next);
}
if (bit != 0)
*q++=(unsigned char) (byte << (8-bit));
if (next->previous == (Image *) NULL)
{
status=SetImageProgress(next,SaveImageTag,y,next->rows);
if (status == MagickFalse)
break;
}
}
break;
}
case 4:
{
size_t
nibble,
byte;
/*
Convert PseudoClass image to a ICON monochrome image.
*/
for (y=0; y < (ssize_t) next->rows; y++)
{
p=GetVirtualPixels(next,0,y,next->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
q=pixels+(next->rows-y-1)*bytes_per_line;
nibble=0;
byte=0;
for (x=0; x < (ssize_t) next->columns; x++)
{
byte<<=4;
byte|=((size_t) GetPixelIndex(next,p) & 0x0f);
nibble++;
if (nibble == 2)
{
*q++=(unsigned char) byte;
nibble=0;
byte=0;
}
p+=GetPixelChannels(next);
}
if (nibble != 0)
*q++=(unsigned char) (byte << 4);
if (next->previous == (Image *) NULL)
{
status=SetImageProgress(next,SaveImageTag,y,next->rows);
if (status == MagickFalse)
break;
}
}
break;
}
case 8:
{
/*
Convert PseudoClass packet to ICON pixel.
*/
for (y=0; y < (ssize_t) next->rows; y++)
{
p=GetVirtualPixels(next,0,y,next->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
q=pixels+(next->rows-y-1)*bytes_per_line;
for (x=0; x < (ssize_t) next->columns; x++)
{
*q++=(unsigned char) GetPixelIndex(next,p);
p+=GetPixelChannels(next);
}
if (next->previous == (Image *) NULL)
{
status=SetImageProgress(next,SaveImageTag,y,next->rows);
if (status == MagickFalse)
break;
}
}
break;
}
case 24:
case 32:
{
/*
Convert DirectClass packet to ICON BGR888 or BGRA8888 pixel.
*/
for (y=0; y < (ssize_t) next->rows; y++)
{
p=GetVirtualPixels(next,0,y,next->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
q=pixels+(next->rows-y-1)*bytes_per_line;
for (x=0; x < (ssize_t) next->columns; x++)
{
*q++=ScaleQuantumToChar(GetPixelBlue(next,p));
*q++=ScaleQuantumToChar(GetPixelGreen(next,p));
*q++=ScaleQuantumToChar(GetPixelRed(next,p));
if (next->alpha_trait == UndefinedPixelTrait)
*q++=ScaleQuantumToChar(QuantumRange);
else
*q++=ScaleQuantumToChar(GetPixelAlpha(next,p));
p+=GetPixelChannels(next);
}
if (icon_info.bits_per_pixel == 24)
for (x=3L*(ssize_t) next->columns; x < (ssize_t) bytes_per_line; x++)
*q++=0x00;
if (next->previous == (Image *) NULL)
{
status=SetImageProgress(next,SaveImageTag,y,next->rows);
if (status == MagickFalse)
break;
}
}
break;
}
}
/*
Write 40-byte version 3+ bitmap header.
*/
icon_file.directory[scene].width=(unsigned char) icon_info.width;
icon_file.directory[scene].height=(unsigned char) icon_info.height;
icon_file.directory[scene].colors=(unsigned char)
icon_info.number_colors;
icon_file.directory[scene].reserved=0;
icon_file.directory[scene].planes=icon_info.planes;
icon_file.directory[scene].bits_per_pixel=icon_info.bits_per_pixel;
icon_file.directory[scene].size=icon_info.size;
icon_file.directory[scene].offset=(size_t) TellBlob(image);
(void) WriteBlobLSBLong(image,(unsigned int) 40);
(void) WriteBlobLSBLong(image,(unsigned int) icon_info.width);
(void) WriteBlobLSBLong(image,(unsigned int) icon_info.height*2);
(void) WriteBlobLSBShort(image,icon_info.planes);
(void) WriteBlobLSBShort(image,icon_info.bits_per_pixel);
(void) WriteBlobLSBLong(image,(unsigned int) icon_info.compression);
(void) WriteBlobLSBLong(image,(unsigned int) icon_info.image_size);
(void) WriteBlobLSBLong(image,(unsigned int) icon_info.x_pixels);
(void) WriteBlobLSBLong(image,(unsigned int) icon_info.y_pixels);
(void) WriteBlobLSBLong(image,(unsigned int) icon_info.number_colors);
(void) WriteBlobLSBLong(image,(unsigned int)
icon_info.colors_important);
if (next->storage_class == PseudoClass)
{
unsigned char
*icon_colormap;
/*
Dump colormap to file.
*/
icon_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
(1UL << icon_info.bits_per_pixel),4UL*sizeof(*icon_colormap));
if (icon_colormap == (unsigned char *) NULL)
{
images=DestroyImageList(images);
ThrowWriterException(ResourceLimitError,
"MemoryAllocationFailed");
}
q=icon_colormap;
for (i=0; i < (ssize_t) next->colors; i++)
{
*q++=ScaleQuantumToChar(next->colormap[i].blue);
*q++=ScaleQuantumToChar(next->colormap[i].green);
*q++=ScaleQuantumToChar(next->colormap[i].red);
*q++=(unsigned char) 0x0;
}
for ( ; i < (ssize_t) (1UL << icon_info.bits_per_pixel); i++)
{
*q++=(unsigned char) 0x00;
*q++=(unsigned char) 0x00;
*q++=(unsigned char) 0x00;
*q++=(unsigned char) 0x00;
}
(void) WriteBlob(image,(size_t) (4UL*(1UL <<
icon_info.bits_per_pixel)),icon_colormap);
icon_colormap=(unsigned char *) RelinquishMagickMemory(
icon_colormap);
}
(void) WriteBlob(image,(size_t) icon_info.image_size,pixels);
pixels=(unsigned char *) RelinquishMagickMemory(pixels);
/*
Write matte mask.
*/
scanline_pad=(((next->columns+31) & ~31)-next->columns) >> 3;
for (y=((ssize_t) next->rows - 1); y >= 0; y--)
{
unsigned char
bit,
byte;
p=GetVirtualPixels(next,0,y,next->columns,1,exception);
if (p == (const Quantum *) NULL)
break;
bit=0;
byte=0;
for (x=0; x < (ssize_t) next->columns; x++)
{
byte<<=1;
if ((next->alpha_trait != UndefinedPixelTrait) &&
(GetPixelAlpha(next,p) == (Quantum) TransparentAlpha))
byte|=0x01;
bit++;
if (bit == 8)
{
(void) WriteBlobByte(image,(unsigned char) byte);
bit=0;
byte=0;
}
p+=GetPixelChannels(next);
}
if (bit != 0)
(void) WriteBlobByte(image,(unsigned char) (byte << (8-bit)));
for (i=0; i < (ssize_t) scanline_pad; i++)
(void) WriteBlobByte(image,(unsigned char) 0);
}
}
if (GetNextImageInList(next) == (Image *) NULL)
break;
status=SetImageProgress(next,SaveImagesTag,scene++,imageListLength);
if (status == MagickFalse)
break;
next=SyncNextImageInList(next);
} while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
offset=SeekBlob(image,0,SEEK_SET);
(void) offset;
(void) WriteBlobLSBShort(image,0);
(void) WriteBlobLSBShort(image,1);
(void) WriteBlobLSBShort(image,(unsigned short) (scene+1));
scene=0;
next=(images != (Image *) NULL) ? images : image;
do
{
(void) WriteBlobByte(image,icon_file.directory[scene].width);
(void) WriteBlobByte(image,icon_file.directory[scene].height);
(void) WriteBlobByte(image,icon_file.directory[scene].colors);
(void) WriteBlobByte(image,icon_file.directory[scene].reserved);
(void) WriteBlobLSBShort(image,icon_file.directory[scene].planes);
(void) WriteBlobLSBShort(image,icon_file.directory[scene].bits_per_pixel);
(void) WriteBlobLSBLong(image,(unsigned int)
icon_file.directory[scene].size);
(void) WriteBlobLSBLong(image,(unsigned int)
icon_file.directory[scene].offset);
scene++;
next=SyncNextImageInList(next);
} while ((next != (Image *) NULL) && (image_info->adjoin != MagickFalse));
(void) CloseBlob(image);
images=DestroyImageList(images);
return(MagickTrue);
}