Files

1590 lines
48 KiB
C

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% W W BBBB IIIII N N FFFFF OOO %
% W W B B I NN N F O O %
% W W W BBBB I N N N FFF O O %
% WW WW B B I N NN F O O %
% W W BBBB IIIII N N F OOO %
% %
% %
% Read Amiga Workbench Icon Image Format %
% %
% Software Design %
% Gareth Davidson %
% March 2026 %
% %
% %
% Copyright @ 1999 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/license/ %
% %
% 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/blob.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache.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/pixel-accessor.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/static.h"
#include "MagickCore/string_.h"
#include "MagickCore/module.h"
#if defined(MAGICKCORE_ZLIB_DELEGATE)
#include <zlib.h>
#endif
/*
Define declarations.
*/
#define AmigaIconMagic 0xE310
#define MaxAmigaIconDimension 1024
#define MaxAmigaIconFormScanSize (64UL*1024UL*1024UL)
#define MaxAmigaIconImageChunks 8
#define MaxAmigaIconScenes 8
#define MaxAmigaIconToolTypes 4096
#define MaxNewIconBitCount \
((size_t) MaxAmigaIconDimension*MaxAmigaIconDimension*16)
/*
Typedef declarations.
*/
typedef enum
{
AmigaIconChunkContinue, /* keep walking the chunk list */
AmigaIconChunkStop, /* malformed chunk: stop, keep images so far */
AmigaIconChunkFatal /* abort the read */
} AmigaIconChunkStatus;
static MagickBooleanType GetRemainingBlobSize(Image *image,size_t *remaining)
{
MagickOffsetType
offset;
MagickSizeType
blob_size;
offset=TellBlob(image);
blob_size=GetBlobSize(image);
if ((offset < 0) || ((MagickSizeType) offset > blob_size) ||
(blob_size-(MagickSizeType) offset > (MagickSizeType) MAGICK_SIZE_MAX))
{
*remaining=0;
return(MagickFalse);
}
*remaining=(size_t) (blob_size-(MagickSizeType) offset);
return(MagickTrue);
}
/*
Amiga Workbench system palettes.
*/
static const unsigned char AmigaWB1x[4][3] =
{
{85,170,255},{255,255,255},{0,0,0},{255,136,0}
};
static const unsigned char AmigaWB2x[8][3] =
{
{149,149,149},{0,0,0},{255,255,255},{59,103,162},
{123,123,123},{175,175,175},{170,144,124},{255,169,151}
};
static unsigned int BitsToUInt(const unsigned char *data,size_t total_bits,
size_t *bit_pos,size_t count)
{
size_t
i;
unsigned int
val;
val=0;
for (i=0; i < count; i++)
{
val<<=1;
if (*bit_pos < total_bits)
{
size_t
bit_idx,
byte_idx;
byte_idx=(*bit_pos) >> 3;
bit_idx=7-((*bit_pos) & 7);
val|=(unsigned int) ((data[byte_idx] >> bit_idx) & 1);
}
(*bit_pos)++;
}
return(val);
}
/*
Count expanded bits from a NewIcon encoded line (for buffer sizing).
*/
static size_t NewIconLineBitCount(const char *src,size_t src_len)
{
size_t
bit_count,
ch_idx;
bit_count=0;
for (ch_idx=0; ch_idx < src_len; ch_idx++)
{
unsigned int
byte;
byte=(unsigned int) (unsigned char) src[ch_idx];
if (byte < 32)
continue;
if (byte <= 208)
bit_count+=7;
else
bit_count+=(size_t) (byte-208)*7;
if (bit_count > MaxNewIconBitCount)
return(bit_count);
}
return(bit_count);
}
/*
Decode one NewIcon 7-bit encoded line into a bit buffer.
Each character maps to 7 bits; characters above 208 are RLE zero runs.
Returns the number of bits written.
*/
static size_t DecodeNewIconLine(const char *src,size_t src_len,
unsigned char *bits,size_t bits_alloc,size_t bit_offset)
{
size_t
bit_count,
ch_idx;
bit_count=bit_offset;
for (ch_idx=0; ch_idx < src_len; ch_idx++)
{
int
bit_i;
unsigned int
byte,
val;
byte=(unsigned int) (unsigned char) src[ch_idx];
if (byte < 32)
continue;
if (byte <= 159)
val=byte-32;
else if (byte <= 208)
val=byte-81;
else
{
bit_count+=(size_t) (byte-208)*7;
continue;
}
for (bit_i=6; bit_i >= 0; bit_i--)
{
if ((bit_count < bits_alloc*8) && ((val >> bit_i) & 1))
bits[bit_count >> 3]|=(unsigned char) (1 << (7-(bit_count & 7)));
bit_count++;
}
}
return(bit_count);
}
/*
Decode GlowIcon/ColorIcon RLE data.
The RLE operates on a bit stream where entries are `depth` bits wide,
but control bytes are always 8 bits.
Returns allocated buffer with decoded bytes (one byte per entry).
Caller must free with RelinquishMagickMemory().
*/
static unsigned char *DecodeAmigaRLE(const unsigned char *data,
size_t data_size,size_t depth,size_t expected_count,size_t *decoded_count,
MagickBooleanType *complete)
{
size_t
bit_pos,
count,
i,
n,
total_bits;
unsigned char
*result;
unsigned int
control,
val;
result=(unsigned char *) AcquireQuantumMemory(expected_count,
sizeof(*result));
if (result == (unsigned char *) NULL)
return((unsigned char *) NULL);
*complete=MagickFalse;
total_bits=data_size*8;
bit_pos=0;
count=0;
while ((bit_pos+8 <= total_bits) && (count < expected_count))
{
control=BitsToUInt(data,total_bits,&bit_pos,8);
if (control == 128)
continue;
if (control <= 127)
{
n=(size_t) control+1;
for (i=0; i < n; i++)
{
if (count >= expected_count)
break;
if (bit_pos+depth > total_bits)
{
*decoded_count=count;
return(result);
}
result[count++]=(unsigned char) BitsToUInt(data,total_bits,&bit_pos,
depth);
}
}
else
{
if (bit_pos+depth > total_bits)
{
*decoded_count=count;
return(result);
}
val=BitsToUInt(data,total_bits,&bit_pos,depth);
n=257-(size_t) control;
for (i=0; i < n; i++)
{
if (count >= expected_count)
break;
result[count++]=(unsigned char) val;
}
}
}
*decoded_count=count;
*complete=count == expected_count ? MagickTrue : MagickFalse;
return(result);
}
static MagickBooleanType AppendAmigaImage(Image **image,size_t width,
size_t height,size_t scene,const ImageInfo *image_info,
ExceptionInfo *exception)
{
if (scene > 0)
{
AcquireNextImage(image_info,*image,exception);
if (GetNextImageInList(*image) == (Image *) NULL)
return(MagickFalse);
*image=SyncNextImageInList(*image);
if (*image == (Image *) NULL)
return(MagickFalse);
}
if (SetImageExtent(*image,width,height,exception) == MagickFalse)
{
if (scene > 0)
DeleteImageFromList(image);
return(MagickFalse);
}
(*image)->depth=8;
(*image)->alpha_trait=BlendPixelTrait;
(*image)->scene=scene;
return(MagickTrue);
}
/*
Render a paletted image (NewIcon or GlowIcon) to DirectClass RGBA.
*/
static MagickBooleanType RenderPalettedAmigaImage(Image *image,size_t width,
size_t height,const unsigned char *pixels,size_t pixel_count,
const unsigned char *palette,size_t pal_entries,
MagickBooleanType has_transparent,unsigned char transparent_idx,
ExceptionInfo *exception)
{
ssize_t
y;
for (y=0; y < (ssize_t) height; y++)
{
Quantum
*q;
ssize_t
x;
q=QueueAuthenticPixels(image,0,y,width,1,exception);
if (q == (Quantum *) NULL)
return(MagickFalse);
for (x=0; x < (ssize_t) width; x++)
{
size_t
pi;
unsigned char
idx;
pi=(size_t) y*width+(size_t) x;
idx=pi < pixel_count ? pixels[pi] : 0;
if ((palette != (const unsigned char *) NULL) &&
((size_t) idx < pal_entries))
{
SetPixelRed(image,ScaleCharToQuantum(palette[idx*3]),q);
SetPixelGreen(image,ScaleCharToQuantum(palette[idx*3+1]),q);
SetPixelBlue(image,ScaleCharToQuantum(palette[idx*3+2]),q);
}
else
{
SetPixelRed(image,0,q);
SetPixelGreen(image,0,q);
SetPixelBlue(image,0,q);
}
if (has_transparent && (idx == transparent_idx))
SetPixelAlpha(image,TransparentAlpha,q);
else
SetPixelAlpha(image,OpaqueAlpha,q);
q+=(ptrdiff_t) GetPixelChannels(image);
}
if (SyncAuthenticPixels(image,exception) == MagickFalse)
return(MagickFalse);
}
return(MagickTrue);
}
/*
Free a bitplane array; NULL entries are ignored.
*/
static unsigned char **RelinquishPlanes(unsigned char **planes,size_t count)
{
size_t
i;
for (i=0; i < count; i++)
planes[i]=(unsigned char *) RelinquishMagickMemory(planes[i]);
return((unsigned char **) RelinquishMagickMemory(planes));
}
/*
Read and render a classic planar Amiga icon image from the blob.
Reads the 20-byte Image header, bitplane data, selects the system palette,
renders to a new scene, and increments *scene. Returns MagickTrue on
success. On failure the blob position is indeterminate.
*/
static MagickBooleanType ReadAndRenderClassicImage(Image **image,
size_t *scene,unsigned int user_data,const ImageInfo *image_info,
ExceptionInfo *exception)
{
const unsigned char
(*palette)[3];
MagickBooleanType
render_status;
size_t
bytes_per_row,
num_colors,
p_idx,
plane_size,
remaining,
words_per_row;
ssize_t
y;
unsigned char
plane_pick,
plane_on_off,
**planes;
unsigned short
depth,
height,
width;
/*
checked ~300k icons and only 64 set left_edge or top_edge, and every one
was corruption that pushed the image out of bounds. So we'll skip those.
*/
(void) ReadBlobMSBShort(*image); /* left_edge */
(void) ReadBlobMSBShort(*image); /* top_edge */
width=ReadBlobMSBShort(*image);
height=ReadBlobMSBShort(*image);
depth=ReadBlobMSBShort(*image);
(void) ReadBlobMSBLong(*image); /* image_data ptr */
plane_pick=(unsigned char) ReadBlobByte(*image);
plane_on_off=(unsigned char) ReadBlobByte(*image);
(void) ReadBlobMSBLong(*image); /* next ptr */
if (EOFBlob(*image) != MagickFalse)
return(MagickFalse);
if ((width == 0) || (height == 0) || (depth == 0) ||
(width > MaxAmigaIconDimension) ||
(height > MaxAmigaIconDimension) || (depth > 8))
return(MagickFalse);
words_per_row=((size_t) width+15)/16;
bytes_per_row=words_per_row*2;
if (HeapOverflowSanityCheckGetSize(bytes_per_row,(size_t) height,
&plane_size) != MagickFalse)
return(MagickFalse);
if ((GetRemainingBlobSize(*image,&remaining) == MagickFalse) ||
(HeapOverflowSanityCheck(plane_size,(size_t) depth) != MagickFalse) ||
(plane_size*(size_t) depth > remaining))
return(MagickFalse);
planes=(unsigned char **) AcquireQuantumMemory((size_t) depth,
sizeof(*planes));
if (planes == (unsigned char **) NULL)
return(MagickFalse);
(void) memset(planes,0,(size_t) depth*sizeof(*planes));
for (p_idx=0; p_idx < (size_t) depth; p_idx++)
{
planes[p_idx]=(unsigned char *) AcquireQuantumMemory(plane_size,
sizeof(*planes[p_idx]));
if ((planes[p_idx] == (unsigned char *) NULL) ||
(ReadBlob(*image,plane_size,planes[p_idx]) != (ssize_t) plane_size))
{
planes=RelinquishPlanes(planes,p_idx);
return(MagickFalse);
}
}
if (user_data & 255)
{
palette=AmigaWB2x;
num_colors=8;
}
else
{
palette=AmigaWB1x;
num_colors=4;
}
if (AppendAmigaImage(image,(size_t) width,(size_t) height,
*scene,image_info,exception) == MagickFalse)
{
planes=RelinquishPlanes(planes,(size_t) depth);
return(MagickFalse);
}
render_status=MagickTrue;
for (y=0; y < (ssize_t) height; y++)
{
Quantum
*q;
ssize_t
x;
q=QueueAuthenticPixels(*image,0,y,(size_t) width,1,exception);
if (q == (Quantum *) NULL)
{
render_status=MagickFalse;
break;
}
for (x=0; x < (ssize_t) width; x++)
{
size_t
bit_idx,
byte_idx,
data_plane,
plane_num;
unsigned int
color_index;
byte_idx=(size_t) y*bytes_per_row+((size_t) x >> 3);
bit_idx=7-((size_t) x & 7);
color_index=0;
data_plane=0;
for (plane_num=0; plane_num < (size_t) depth; plane_num++)
{
if (plane_pick & (1 << plane_num))
{
if (data_plane < (size_t) depth)
color_index|=(unsigned int)
(((planes[data_plane][byte_idx] >> bit_idx) & 1) << plane_num);
data_plane++;
}
else if (plane_on_off & (1 << plane_num))
color_index|=(1U << plane_num);
}
if (color_index < num_colors)
{
SetPixelRed(*image,ScaleCharToQuantum(palette[color_index][0]),q);
SetPixelGreen(*image,ScaleCharToQuantum(palette[color_index][1]),q);
SetPixelBlue(*image,ScaleCharToQuantum(palette[color_index][2]),q);
}
else
{
SetPixelRed(*image,0,q);
SetPixelGreen(*image,0,q);
SetPixelBlue(*image,0,q);
}
if (color_index == 0)
SetPixelAlpha(*image,TransparentAlpha,q);
else
SetPixelAlpha(*image,OpaqueAlpha,q);
q+=(ptrdiff_t) GetPixelChannels(*image);
}
if (SyncAuthenticPixels(*image,exception) == MagickFalse)
{
render_status=MagickFalse;
break;
}
}
planes=RelinquishPlanes(planes,(size_t) depth);
if (render_status == MagickFalse)
{
if (*scene > 0)
DeleteImageFromList(image);
return(MagickFalse);
}
(*scene)++;
return(MagickTrue);
}
/*
Decode and render a NewIcon image from collected IM1=/IM2= lines.
Returns MagickTrue if an image was successfully decoded and rendered.
*/
static MagickBooleanType DecodeAndRenderNewIcon(Image **image,size_t *scene,
char **im_lines,size_t im_count,const ImageInfo *image_info,
ExceptionInfo *exception)
{
char
*first_line;
size_t
bit_count,
bit_pos,
line_bits_alloc,
line_bits_bytes,
line_idx,
ni_depth,
ni_height,
ni_num_colors,
ni_transparent,
ni_width,
pal_idx,
pix_count;
unsigned char
*line_bits,
*ni_palette,
*ni_pixels;
if ((im_count == 0) || (im_lines == (char **) NULL))
return(MagickFalse);
first_line=im_lines[0];
if (strlen(first_line) < 5)
return(MagickFalse);
ni_transparent=((unsigned char) first_line[0] == 66) ? 1 : 0;
if (((unsigned char) first_line[1] < 33) ||
((unsigned char) first_line[2] < 33) ||
((unsigned char) first_line[3] < 33) ||
((unsigned char) first_line[4] < 33))
return(MagickFalse);
ni_width=(size_t) ((unsigned char) first_line[1]-33);
ni_height=(size_t) ((unsigned char) first_line[2]-33);
ni_num_colors=(size_t) (((unsigned char) first_line[3]-33) << 6)+
(size_t) ((unsigned char) first_line[4]-33);
if ((ni_width == 0) || (ni_height == 0) || (ni_num_colors == 0) ||
(ni_width > MaxAmigaIconDimension) ||
(ni_height > MaxAmigaIconDimension) ||
(ni_num_colors > 256))
return(MagickFalse);
ni_depth=1;
while (((size_t) 1 << ni_depth) < ni_num_colors)
ni_depth++;
/*
Find max expanded bit count to size the reusable decode buffer.
*/
line_bits_alloc=NewIconLineBitCount(first_line+5,strlen(first_line+5));
for (line_idx=1; line_idx < im_count; line_idx++)
{
size_t
ll;
ll=NewIconLineBitCount(im_lines[line_idx],strlen(im_lines[line_idx]));
if (ll > line_bits_alloc)
line_bits_alloc=ll;
}
line_bits_alloc+=7;
if (line_bits_alloc > MaxNewIconBitCount)
return(MagickFalse);
line_bits_bytes=(line_bits_alloc+7)/8+1;
line_bits=(unsigned char *) AcquireQuantumMemory(line_bits_bytes,
sizeof(*line_bits));
if (line_bits == (unsigned char *) NULL)
return(MagickFalse);
/*
First line: extract palette only, discard remaining bits.
*/
(void) memset(line_bits,0,line_bits_bytes*sizeof(*line_bits));
bit_count=DecodeNewIconLine(first_line+5,strlen(first_line+5),line_bits,
line_bits_bytes,0);
if (bit_count < ni_num_colors*24)
{
line_bits=(unsigned char *) RelinquishMagickMemory(line_bits);
return(MagickFalse);
}
ni_palette=(unsigned char *) AcquireQuantumMemory(ni_num_colors*3+3,
sizeof(*ni_palette));
if (ni_palette == (unsigned char *) NULL)
{
line_bits=(unsigned char *) RelinquishMagickMemory(line_bits);
return(MagickFalse);
}
(void) memset(ni_palette,0,(ni_num_colors*3+3)*sizeof(*ni_palette));
bit_pos=0;
for (pal_idx=0; pal_idx < ni_num_colors; pal_idx++)
{
ni_palette[pal_idx*3]=(unsigned char) BitsToUInt(line_bits,bit_count,
&bit_pos,8);
ni_palette[pal_idx*3+1]=(unsigned char) BitsToUInt(line_bits,bit_count,
&bit_pos,8);
ni_palette[pal_idx*3+2]=(unsigned char) BitsToUInt(line_bits,bit_count,
&bit_pos,8);
}
/*
Subsequent lines: extract pixels per-line, discard leftover bits.
*/
ni_pixels=(unsigned char *) AcquireQuantumMemory(ni_width*ni_height+1,
sizeof(*ni_pixels));
if (ni_pixels == (unsigned char *) NULL)
{
ni_palette=(unsigned char *) RelinquishMagickMemory(ni_palette);
line_bits=(unsigned char *) RelinquishMagickMemory(line_bits);
return(MagickFalse);
}
(void) memset(ni_pixels,0,(ni_width*ni_height+1)*sizeof(*ni_pixels));
pix_count=0;
for (line_idx=1; line_idx < im_count &&
pix_count < ni_width*ni_height; line_idx++)
{
size_t
lbp;
(void) memset(line_bits,0,line_bits_bytes*sizeof(*line_bits));
bit_count=DecodeNewIconLine(im_lines[line_idx],strlen(im_lines[line_idx]),
line_bits,line_bits_bytes,0);
lbp=0;
while ((lbp+ni_depth <= bit_count) && (pix_count < ni_width*ni_height))
ni_pixels[pix_count++]=(unsigned char) BitsToUInt(line_bits,bit_count,
&lbp,ni_depth);
}
line_bits=(unsigned char *) RelinquishMagickMemory(line_bits);
if (pix_count < ni_width*ni_height)
{
ni_pixels=(unsigned char *) RelinquishMagickMemory(ni_pixels);
ni_palette=(unsigned char *) RelinquishMagickMemory(ni_palette);
return(MagickFalse);
}
/*
Render NewIcon to image.
*/
if (AppendAmigaImage(image,ni_width,ni_height,*scene,image_info,
exception) == MagickFalse)
{
ni_pixels=(unsigned char *) RelinquishMagickMemory(ni_pixels);
ni_palette=(unsigned char *) RelinquishMagickMemory(ni_palette);
return(MagickFalse);
}
if (RenderPalettedAmigaImage(*image,ni_width,ni_height,ni_pixels,pix_count,
ni_palette,ni_num_colors,ni_transparent ? MagickTrue : MagickFalse,0,
exception) == MagickFalse)
{
ni_pixels=(unsigned char *) RelinquishMagickMemory(ni_pixels);
ni_palette=(unsigned char *) RelinquishMagickMemory(ni_palette);
if (*scene > 0)
DeleteImageFromList(image);
return(MagickFalse);
}
ni_pixels=(unsigned char *) RelinquishMagickMemory(ni_pixels);
ni_palette=(unsigned char *) RelinquishMagickMemory(ni_palette);
(*scene)++;
return(MagickTrue);
}
/*
Read the ToolTypes string table and decode NewIcon (IM1=/IM2=) images
found in it. Returns AmigaIconChunkContinue on success,
AmigaIconChunkStop when the table is truncated (keep the images decoded
so far) and AmigaIconChunkFatal on hard failures (exception is set).
*/
static AmigaIconChunkStatus ReadToolTypesNewIcons(Image **image,size_t *scene,
const ImageInfo *image_info,ExceptionInfo *exception)
{
char
**im1_lines,
**im2_lines,
**tooltypes;
MagickBooleanType
newicon_failure,
tooltypes_corrupt,
tooltypes_resource_failure;
size_t
im1_alloc,
im1_count,
im2_alloc,
im2_count,
num_entries,
remaining,
tt_idx;
unsigned int
count_field;
if (EOFBlob(*image) != MagickFalse)
return(AmigaIconChunkStop);
count_field=ReadBlobMSBLong(*image);
if (GetRemainingBlobSize(*image,&remaining) == MagickFalse)
return(AmigaIconChunkStop);
if (count_field < 8)
num_entries=0;
else
num_entries=(size_t) (count_field/4-1);
if ((num_entries > remaining/4) || (num_entries > MaxAmigaIconToolTypes))
return(AmigaIconChunkStop);
tooltypes=(char **) NULL;
if (num_entries > 0)
{
tooltypes=(char **) AcquireQuantumMemory(num_entries,
sizeof(*tooltypes));
if (tooltypes == (char **) NULL)
{
(void) ThrowMagickException(exception,GetMagickModule(),
ResourceLimitError,"MemoryAllocationFailed","`%s'",
(*image)->filename);
return(AmigaIconChunkFatal);
}
(void) memset(tooltypes,0,num_entries*sizeof(*tooltypes));
}
/*
Read all tooltype strings, collect IM1=/IM2= lines.
*/
newicon_failure=MagickFalse;
tooltypes_corrupt=MagickFalse;
tooltypes_resource_failure=MagickFalse;
im1_lines=(char **) NULL;
im2_lines=(char **) NULL;
im1_count=0;
im1_alloc=0;
im2_count=0;
im2_alloc=0;
for (tt_idx=0; tt_idx < num_entries; tt_idx++)
{
unsigned int
text_len;
if (EOFBlob(*image) != MagickFalse)
break;
text_len=ReadBlobMSBLong(*image);
if ((GetRemainingBlobSize(*image,&remaining) == MagickFalse) ||
(text_len == 0) || ((size_t) text_len > remaining) ||
(text_len == UINT_MAX))
{
tooltypes_corrupt=MagickTrue;
break;
}
tooltypes[tt_idx]=(char *) AcquireQuantumMemory((size_t) text_len+1,
sizeof(char));
if (tooltypes[tt_idx] == (char *) NULL)
{
tooltypes_resource_failure=MagickTrue;
break;
}
if (ReadBlob(*image,text_len,(unsigned char *) tooltypes[tt_idx]) !=
(ssize_t) text_len)
{
tooltypes[tt_idx]=(char *) RelinquishMagickMemory(tooltypes[tt_idx]);
tooltypes[tt_idx]=(char *) NULL;
tooltypes_corrupt=MagickTrue;
break;
}
tooltypes[tt_idx][text_len]='\0';
if (strncmp(tooltypes[tt_idx],"IM1=",4) == 0)
{
if (im1_count >= im1_alloc)
{
char
**new_lines;
im1_alloc=im1_alloc == 0 ? 16 : im1_alloc*2;
new_lines=(char **) ResizeQuantumMemory(im1_lines,im1_alloc,
sizeof(*im1_lines));
if (new_lines == (char **) NULL)
{
im1_lines=(char **) NULL;
tooltypes_resource_failure=MagickTrue;
break;
}
im1_lines=new_lines;
}
im1_lines[im1_count++]=tooltypes[tt_idx]+4;
}
else if (strncmp(tooltypes[tt_idx],"IM2=",4) == 0)
{
if (im2_count >= im2_alloc)
{
char
**new_lines;
im2_alloc=im2_alloc == 0 ? 16 : im2_alloc*2;
new_lines=(char **) ResizeQuantumMemory(im2_lines,im2_alloc,
sizeof(*im2_lines));
if (new_lines == (char **) NULL)
{
im2_lines=(char **) NULL;
tooltypes_resource_failure=MagickTrue;
break;
}
im2_lines=new_lines;
}
im2_lines[im2_count++]=tooltypes[tt_idx]+4;
}
}
/*
Decode NewIcon from IM1= and IM2= lines.
*/
if ((tooltypes_corrupt == MagickFalse) &&
(tooltypes_resource_failure == MagickFalse))
{
if ((DecodeAndRenderNewIcon(image,scene,im1_lines,im1_count,
image_info,exception) == MagickFalse) &&
(exception->severity >= ErrorException))
newicon_failure=MagickTrue;
if ((newicon_failure == MagickFalse) &&
(DecodeAndRenderNewIcon(image,scene,im2_lines,im2_count,
image_info,exception) == MagickFalse) &&
(exception->severity >= ErrorException))
newicon_failure=MagickTrue;
}
if (im1_lines != (char **) NULL)
im1_lines=(char **) RelinquishMagickMemory(im1_lines);
if (im2_lines != (char **) NULL)
im2_lines=(char **) RelinquishMagickMemory(im2_lines);
if (tooltypes != (char **) NULL)
{
for (tt_idx=0; tt_idx < num_entries; tt_idx++)
if (tooltypes[tt_idx] != (char *) NULL)
tooltypes[tt_idx]=(char *)
RelinquishMagickMemory(tooltypes[tt_idx]);
tooltypes=(char **) RelinquishMagickMemory(tooltypes);
}
if (tooltypes_resource_failure != MagickFalse)
{
(void) ThrowMagickException(exception,GetMagickModule(),
ResourceLimitError,"MemoryAllocationFailed","`%s'",
(*image)->filename);
return(AmigaIconChunkFatal);
}
if (tooltypes_corrupt != MagickFalse)
return(AmigaIconChunkStop);
if (newicon_failure != MagickFalse)
return(AmigaIconChunkFatal);
return(AmigaIconChunkContinue);
}
/*
Decode and render one GlowIcon IMAG chunk. A palette-less IMAG reuses
the previous chunk's palette via last_pal_data and last_pal_count; a
rendered chunk's own palette replaces the saved one.
*/
static AmigaIconChunkStatus DecodeAndRenderGlowIcon(Image **image,
size_t *scene,const unsigned char *chunk,size_t chunk_size,
size_t icon_width,size_t icon_height,unsigned char **last_pal_data,
size_t *last_pal_count,const ImageInfo *image_info,
ExceptionInfo *exception)
{
const unsigned char
*render_pal;
MagickBooleanType
rle_complete,
status;
size_t
image_size,
num_pal_colors,
pal_count,
palette_size,
pixel_count,
render_pal_entries;
unsigned char
has_palette,
has_transparent,
imag_depth,
imag_flags,
image_format,
num_colors_m1,
*pal_data,
palette_format,
*pixel_data,
transparent_color;
if ((chunk_size < 10) || (icon_width == 0) || (icon_height == 0) ||
(icon_width > MaxAmigaIconDimension) ||
(icon_height > MaxAmigaIconDimension))
return(AmigaIconChunkContinue);
transparent_color=chunk[0];
num_colors_m1=chunk[1];
imag_flags=chunk[2];
image_format=chunk[3];
palette_format=chunk[4];
imag_depth=chunk[5];
image_size=(((size_t) chunk[6] << 8) | (size_t) chunk[7])+1;
palette_size=(((size_t) chunk[8] << 8) | (size_t) chunk[9])+1;
has_transparent=(imag_flags & 1) ? 1 : 0;
has_palette=(imag_flags & 2) ? 1 : 0;
num_pal_colors=(size_t) num_colors_m1+1;
if ((imag_depth == 0) || (imag_depth > 8) || (image_size > chunk_size-10))
return(AmigaIconChunkStop);
/*
Decode image pixels.
*/
if (image_format == 1)
{
rle_complete=MagickFalse;
pixel_data=DecodeAmigaRLE(chunk+10,image_size,(size_t) imag_depth,
icon_width*icon_height,&pixel_count,&rle_complete);
if ((pixel_data != (unsigned char *) NULL) &&
(rle_complete == MagickFalse))
{
pixel_data=(unsigned char *) RelinquishMagickMemory(pixel_data);
return(AmigaIconChunkStop);
}
}
else
{
pixel_count=image_size;
if (pixel_count > icon_width*icon_height)
pixel_count=icon_width*icon_height;
pixel_data=(unsigned char *) AcquireQuantumMemory(pixel_count+1,
sizeof(*pixel_data));
if (pixel_data != (unsigned char *) NULL)
(void) memcpy(pixel_data,chunk+10,pixel_count);
}
if (pixel_data == (unsigned char *) NULL)
return(AmigaIconChunkStop);
/*
Decode palette.
*/
pal_data=(unsigned char *) NULL;
pal_count=0;
if (has_palette && (palette_size <= chunk_size-10-image_size))
{
if (palette_format == 1)
{
rle_complete=MagickFalse;
pal_data=DecodeAmigaRLE(chunk+10+image_size,palette_size,8,
num_pal_colors*3,&pal_count,&rle_complete);
if ((pal_data != (unsigned char *) NULL) &&
(rle_complete == MagickFalse))
{
pal_data=(unsigned char *) RelinquishMagickMemory(pal_data);
pixel_data=(unsigned char *)
RelinquishMagickMemory(pixel_data);
return(AmigaIconChunkStop);
}
}
else
{
pal_count=palette_size;
if (pal_count > num_pal_colors*3)
pal_count=num_pal_colors*3;
pal_data=(unsigned char *) AcquireQuantumMemory(pal_count+1,
sizeof(*pal_data));
if (pal_data != (unsigned char *) NULL)
(void) memcpy(pal_data,chunk+10+image_size,pal_count);
}
}
if (pal_data != (unsigned char *) NULL)
{
render_pal=pal_data;
render_pal_entries=pal_count/3;
}
else
{
render_pal=*last_pal_data;
render_pal_entries=*last_pal_count/3;
}
status=AppendAmigaImage(image,icon_width,icon_height,*scene,image_info,
exception);
if (status != MagickFalse)
status=RenderPalettedAmigaImage(*image,icon_width,icon_height,pixel_data,
pixel_count,render_pal,render_pal_entries,has_transparent ? MagickTrue :
MagickFalse,transparent_color,exception);
pixel_data=(unsigned char *) RelinquishMagickMemory(pixel_data);
if (status == MagickFalse)
{
if (pal_data != (unsigned char *) NULL)
pal_data=(unsigned char *) RelinquishMagickMemory(pal_data);
return(AmigaIconChunkFatal);
}
if (pal_data != (unsigned char *) NULL)
{
if (*last_pal_data != (unsigned char *) NULL)
*last_pal_data=(unsigned char *)
RelinquishMagickMemory(*last_pal_data);
*last_pal_data=pal_data;
*last_pal_count=pal_count;
}
(*scene)++;
return(AmigaIconChunkContinue);
}
#if defined(MAGICKCORE_ZLIB_DELEGATE)
/*
Decode and render one zlib-compressed ARGB chunk (OS4-style icons).
*/
static AmigaIconChunkStatus DecodeAndRenderARGBIcon(Image **image,
size_t *scene,const unsigned char *chunk,size_t chunk_size,
size_t icon_width,size_t icon_height,const ImageInfo *image_info,
ExceptionInfo *exception)
{
ssize_t
y;
uLongf
raw_size;
unsigned char
*argb_raw;
if ((chunk_size <= 10) || (icon_width == 0) || (icon_height == 0) ||
(icon_width > MaxAmigaIconDimension) ||
(icon_height > MaxAmigaIconDimension))
return(AmigaIconChunkContinue);
raw_size=(uLongf) (icon_width*icon_height*4);
argb_raw=(unsigned char *) AcquireQuantumMemory((size_t) raw_size+1,
sizeof(*argb_raw));
if (argb_raw == (unsigned char *) NULL)
return(AmigaIconChunkContinue);
if (uncompress(argb_raw,&raw_size,chunk+10,(uLong) (chunk_size-10)) != Z_OK)
{
argb_raw=(unsigned char *) RelinquishMagickMemory(argb_raw);
return(AmigaIconChunkContinue);
}
if (AppendAmigaImage(image,icon_width,icon_height,*scene,image_info,
exception) == MagickFalse)
{
argb_raw=(unsigned char *) RelinquishMagickMemory(argb_raw);
return(AmigaIconChunkFatal);
}
for (y=0; y < (ssize_t) icon_height; y++)
{
Quantum
*q;
ssize_t
x;
q=QueueAuthenticPixels(*image,0,y,icon_width,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) icon_width; x++)
{
size_t
pi;
pi=((size_t) y*icon_width+(size_t) x)*4;
if (pi+3 < (size_t) raw_size)
{
SetPixelRed(*image,ScaleCharToQuantum(argb_raw[pi+1]),q);
SetPixelGreen(*image,ScaleCharToQuantum(argb_raw[pi+2]),q);
SetPixelBlue(*image,ScaleCharToQuantum(argb_raw[pi+3]),q);
SetPixelAlpha(*image,ScaleCharToQuantum(argb_raw[pi]),q);
}
else
{
SetPixelRed(*image,0,q);
SetPixelGreen(*image,0,q);
SetPixelBlue(*image,0,q);
SetPixelAlpha(*image,TransparentAlpha,q);
}
q+=(ptrdiff_t) GetPixelChannels(*image);
}
if (SyncAuthenticPixels(*image,exception) == MagickFalse)
break;
}
argb_raw=(unsigned char *) RelinquishMagickMemory(argb_raw);
if (y < (ssize_t) icon_height)
return(AmigaIconChunkFatal);
(*scene)++;
return(AmigaIconChunkContinue);
}
#endif
/*
Walk the IFF chunks of a FORM ICON (FACE, IMAG, ARGB) and render each
image found. Returns MagickFalse only on a fatal error; a malformed
chunk stops the walk but keeps any images already decoded.
*/
static MagickBooleanType ParseFormIconChunks(Image **image,size_t *scene,
const unsigned char *chunks,size_t chunks_size,const ImageInfo *image_info,
ExceptionInfo *exception)
{
AmigaIconChunkStatus
chunk_status;
size_t
chunk_pos,
image_chunks,
icon_height,
icon_width,
last_pal_count;
unsigned char
*last_pal_data;
chunk_pos=0;
image_chunks=0;
icon_width=0;
icon_height=0;
last_pal_data=(unsigned char *) NULL;
last_pal_count=0;
chunk_status=AmigaIconChunkContinue;
while (chunk_pos+8 <= chunks_size)
{
const unsigned char
*chunk_id;
size_t
chunk_data,
chunk_size;
chunk_id=chunks+chunk_pos;
chunk_size=((size_t) chunks[chunk_pos+4] << 24) |
((size_t) chunks[chunk_pos+5] << 16) |
((size_t) chunks[chunk_pos+6] << 8) |
(size_t) chunks[chunk_pos+7];
chunk_data=chunk_pos+8;
if ((chunk_size == 0) || (chunk_size > chunks_size-chunk_data))
break;
chunk_pos=chunk_data+chunk_size;
if (chunk_size & 1)
chunk_pos++; /* IFF padding */
if (memcmp(chunk_id,"FACE",4) == 0)
{
if (chunk_size >= 4)
{
icon_width=(size_t) chunks[chunk_data]+1;
icon_height=(size_t) chunks[chunk_data+1]+1;
}
}
else if (memcmp(chunk_id,"IMAG",4) == 0)
{
if ((*scene >= MaxAmigaIconScenes) ||
(image_chunks >= MaxAmigaIconImageChunks))
chunk_status=AmigaIconChunkStop;
else
{
image_chunks++;
chunk_status=DecodeAndRenderGlowIcon(image,scene,
chunks+chunk_data,chunk_size,icon_width,icon_height,
&last_pal_data,&last_pal_count,image_info,exception);
}
}
#if defined(MAGICKCORE_ZLIB_DELEGATE)
else if (memcmp(chunk_id,"ARGB",4) == 0)
{
if ((*scene >= MaxAmigaIconScenes) ||
(image_chunks >= MaxAmigaIconImageChunks))
chunk_status=AmigaIconChunkStop;
else
{
image_chunks++;
chunk_status=DecodeAndRenderARGBIcon(image,scene,
chunks+chunk_data,chunk_size,icon_width,icon_height,image_info,
exception);
}
}
#endif
if (chunk_status != AmigaIconChunkContinue)
break;
}
if (last_pal_data != (unsigned char *) NULL)
last_pal_data=(unsigned char *) RelinquishMagickMemory(last_pal_data);
return(chunk_status == AmigaIconChunkFatal ? MagickFalse : MagickTrue);
}
/*
Finish a read whose trailing sections (text fields, tooltypes or
DrawerData2) are truncated. Real-world icons are often cut short after
the image data, so keep the images already decoded and warn.
*/
static Image *FinishTruncatedWBINFO(Image *image,const ImageInfo *image_info,
ExceptionInfo *exception)
{
(void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
"InsufficientImageDataInFile","`%s'",image_info->filename);
if (CloseBlob(image) == MagickFalse)
return(DestroyImageList(image));
return(GetFirstImageInList(image));
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s W B I N F O %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% IsWBINFO() returns MagickTrue if the image format type, identified by
% the magick string, is an Amiga Workbench icon file.
%
% The format of the IsWBINFO method is:
%
% MagickBooleanType IsWBINFO(const unsigned char *magick,
% const size_t length)
%
*/
static MagickBooleanType IsWBINFO(const unsigned char *magick,
const size_t length)
{
if (length < 2)
return(MagickFalse);
if ((magick[0] == 0xE3) && (magick[1] == 0x10))
return(MagickTrue);
return(MagickFalse);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d W B I N F O I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ReadWBINFOImage() reads an Amiga Workbench .info icon file and returns
% it as one or more images. Each icon generation (Classic, NewIcon,
% GlowIcon, ARGB) becomes a separate image in the returned list.
%
% The format of the ReadWBINFOImage method is:
%
% Image *ReadWBINFOImage(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 *ReadWBINFOImage(const ImageInfo *image_info,
ExceptionInfo *exception)
{
Image
*image;
MagickBooleanType
status;
size_t
scene;
unsigned int
gadget_render,
has_default_tool,
has_drawer_data,
has_tool_window,
has_tooltypes,
select_render,
user_data;
unsigned short
magic;
/*
Open image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickCoreSignature);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s",
image_info->filename);
image=AcquireImage(image_info,exception);
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
if (status == MagickFalse)
{
image=DestroyImageList(image);
return((Image *) NULL);
}
/*
Read 78-byte DiskObject header (big-endian).
*/
magic=ReadBlobMSBShort(image);
if (magic != AmigaIconMagic)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
/*
Skip version(2) and the embedded Gadget up to gadget_render: next(4),
left_edge(2), top_edge(2), width(2), height(2), flags(2), activation(2),
gadget_type(2).
*/
if (SeekBlob(image,20,SEEK_CUR) < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
gadget_render=ReadBlobMSBLong(image);
select_render=ReadBlobMSBLong(image);
/* gadget_text(4), mutual_exclude(4), special_info(4), gadget_id(2) */
if (SeekBlob(image,14,SEEK_CUR) < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
user_data=ReadBlobMSBLong(image);
/* End of embedded Gadget, now at offset 48: icon_type(1), padding(1) */
if (SeekBlob(image,2,SEEK_CUR) < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
has_default_tool=ReadBlobMSBLong(image);
has_tooltypes=ReadBlobMSBLong(image);
/* current_x(4), current_y(4) */
if (SeekBlob(image,8,SEEK_CUR) < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
has_drawer_data=ReadBlobMSBLong(image);
has_tool_window=ReadBlobMSBLong(image);
/* stack_size(4), now at offset 78 */
if (SeekBlob(image,4,SEEK_CUR) < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
if (EOFBlob(image) != MagickFalse)
ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
scene=0;
/*
Skip DrawerData (56 bytes) if present.
*/
if (has_drawer_data)
{
if (SeekBlob(image,56,SEEK_CUR) < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
}
/*
Read classic planar image(s).
*/
if (gadget_render)
{
if (ReadAndRenderClassicImage(&image,&scene,user_data,image_info,
exception) == MagickFalse)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
}
if (select_render)
{
if (ReadAndRenderClassicImage(&image,&scene,user_data,image_info,
exception) == MagickFalse)
{
if (scene == 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
return(FinishTruncatedWBINFO(image,image_info,exception));
}
}
/*
Skip DefaultTool if present.
*/
if (has_default_tool)
{
unsigned int
text_len;
text_len=ReadBlobMSBLong(image);
if (SeekBlob(image,(MagickOffsetType) text_len,SEEK_CUR) < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
}
/*
Read ToolTypes and decode NewIcons if present.
*/
if (has_tooltypes)
{
AmigaIconChunkStatus
tooltypes_status;
tooltypes_status=ReadToolTypesNewIcons(&image,&scene,image_info,
exception);
if (tooltypes_status == AmigaIconChunkFatal)
return(DestroyImageList(image));
if (tooltypes_status == AmigaIconChunkStop)
{
if (scene == 0)
ThrowReaderException(CorruptImageError,
"InsufficientImageDataInFile");
return(FinishTruncatedWBINFO(image,image_info,exception));
}
}
/*
Skip ToolWindow if present.
*/
if (has_tool_window)
{
unsigned int
text_len;
text_len=ReadBlobMSBLong(image);
if (SeekBlob(image,(MagickOffsetType) text_len,SEEK_CUR) < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
}
/*
Skip DrawerData2 (6 bytes) if present.
*/
if (has_drawer_data && (user_data & 255))
{
if (SeekBlob(image,6,SEEK_CUR) < 0)
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
}
/*
Scan for FORM ICON (GlowIcons and ARGB images).
Read a bounded tail buffer and search for "FORM" + "ICON".
*/
{
size_t
remaining;
if ((GetRemainingBlobSize(image,&remaining) != MagickFalse) &&
(remaining > 16) && (remaining <= MaxAmigaIconFormScanSize))
{
unsigned char
*rest_data;
rest_data=(unsigned char *) AcquireQuantumMemory(remaining,
sizeof(*rest_data));
if (rest_data != (unsigned char *) NULL)
{
ssize_t
bytes_read;
bytes_read=ReadBlob(image,remaining,rest_data);
if (bytes_read > 12)
{
size_t
search_pos;
for (search_pos=0; search_pos+12 <= (size_t) bytes_read;
search_pos++)
{
size_t
form_size;
if ((memcmp(rest_data+search_pos,"FORM",4) != 0) ||
(memcmp(rest_data+search_pos+8,"ICON",4) != 0))
continue;
form_size=((size_t) rest_data[search_pos+4] << 24) |
((size_t) rest_data[search_pos+5] << 16) |
((size_t) rest_data[search_pos+6] << 8) |
(size_t) rest_data[search_pos+7];
if (form_size > (size_t) bytes_read-search_pos-8)
continue; /* truncated FORM, skip */
if ((form_size >= 4) &&
(ParseFormIconChunks(&image,&scene,
rest_data+search_pos+12,form_size-4,image_info,
exception) == MagickFalse))
{
rest_data=(unsigned char *)
RelinquishMagickMemory(rest_data);
return(DestroyImageList(image));
}
break; /* only process first FORM ICON */
}
}
rest_data=(unsigned char *) RelinquishMagickMemory(rest_data);
}
}
}
if (scene == 0)
ThrowReaderException(ImageError,"NoImagesWereFound");
if (CloseBlob(image) == MagickFalse)
return(DestroyImageList(image));
return(GetFirstImageInList(image));
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e g i s t e r W B I N F O I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% RegisterWBINFOImage() adds attributes for the Amiga Workbench Icon format to
% the list of supported formats.
%
% The format of the RegisterWBINFOImage method is:
%
% size_t RegisterWBINFOImage(void)
%
*/
ModuleExport size_t RegisterWBINFOImage(void)
{
MagickInfo
*entry;
entry=AcquireMagickInfo("WBINFO","WBINFO","Amiga Workbench Icon");
entry->decoder=(DecodeImageHandler *) ReadWBINFOImage;
entry->magick=(IsImageFormatHandler *) IsWBINFO;
entry->flags^=CoderAdjoinFlag;
entry->flags|=CoderDecoderSeekableStreamFlag;
entry->mime_type=ConstantString("image/x-amiga-icon");
(void) RegisterMagickInfo(entry);
return(MagickImageCoderSignature);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% U n r e g i s t e r W B I N F O I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% UnregisterWBINFOImage() removes format registrations made by the
% WBINFO module from the list of supported formats.
%
% The format of the UnregisterWBINFOImage method is:
%
% UnregisterWBINFOImage(void)
%
*/
ModuleExport void UnregisterWBINFOImage(void)
{
(void) UnregisterMagickInfo("WBINFO");
}