mirror of
https://github.com/ImageMagick/ImageMagick.git
synced 2026-10-19 05:18:28 +02:00
1590 lines
48 KiB
C
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");
|
|
}
|