/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- IsPNM
- PNMInteger
- ReadPNMImage
- RegisterPNMImage
- UnregisterPNMImage
- WritePNMImage
/*
% Copyright (C) 2003-2010 GraphicsMagick Group
% Copyright (C) 2002 ImageMagick Studio
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
%
% This program is covered by multiple licenses, which are described in
% Copyright.txt. You should have received a copy of Copyright.txt with this
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% PPPP N N M M %
% P P NN N MM MM %
% PPPP N N N M M M %
% P N NN M M %
% P N N M M %
% %
% %
% Read/Write PBMPlus Portable Anymap Image Format. %
% %
% %
% Software Design %
% John Cristy %
% July 1992 %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/
/*
Include declarations.
*/
#include "magick/studio.h"
#include "magick/analyze.h"
#include "magick/attribute.h"
#include "magick/blob.h"
#include "magick/color.h"
#include "magick/colormap.h"
#include "magick/constitute.h"
#include "magick/log.h"
#include "magick/magick.h"
#include "magick/monitor.h"
#include "magick/omp_data_view.h"
#include "magick/pixel_cache.h"
#include "magick/utility.h"
/*
Forward declarations.
*/
static unsigned int
WritePNMImage(const ImageInfo *,Image *);
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s P N M %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Method IsPNM returns True if the image format type, identified by the
% magick string, is PNM.
%
% The format of the IsPNM method is:
%
% unsigned int IsPNM(const unsigned char *magick,const size_t length)
%
% A description of each parameter follows:
%
% o status: Method IsPNM returns True if the image format type is PNM.
%
% o magick: This string is generally the first few bytes of an image file
% or blob.
%
% o length: Specifies the length of the magick string.
%
%
*/
static unsigned int IsPNM(const unsigned char *magick,const size_t length)
{
if (length < 2)
return(False);
if ((*magick == 'P') && isdigit((int) magick[1]))
return(True);
return(False);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d P N M I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Method ReadPNMImage reads a Portable Anymap 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 ReadPNMImage method is:
%
% Image *ReadPNMImage(const ImageInfo *image_info,ExceptionInfo *exception)
%
% A description of each parameter follows:
%
% o image: Method ReadPNMImage returns a pointer to the image after
% reading. A null image is returned if there is a memory shortage or
% if the image cannot be read.
%
% o image_info: Specifies a pointer to a ImageInfo structure.
%
% o exception: return any errors or warnings in this structure.
%
%
*/
static unsigned int PNMInteger(Image *image,const unsigned int base)
{
#define P7Comment "END_OF_COMMENTS\n"
int
c;
unsigned long
value;
/*
Skip any leading whitespace.
*/
do
{
c=ReadBlobByte(image);
if (c == EOF)
return(0);
if (c == '#')
{
char
*comment;
ExtendedSignedIntegralType
offset;
register char
*p,
*q;
size_t
length;
/*
Read comment.
*/
length=MaxTextExtent;
comment=MagickAllocateMemory(char *,length+strlen(P7Comment)+1);
p=comment;
offset=p-comment;
if (comment != (char *) NULL)
for ( ; (c != EOF) && (c != '\n'); p++)
{
if ((size_t) (p-comment) >= length)
{
length<<=1;
length+=MaxTextExtent;
MagickReallocMemory(char *,comment,length+strlen(P7Comment)+1);
if (comment == (char *) NULL)
break;
p=comment+strlen(comment);
}
c=ReadBlobByte(image);
*p=c;
*(p+1)='\0';
}
if (comment == (char *) NULL)
return(0);
q=comment+offset;
if (LocaleCompare(q,P7Comment) == 0)
*q='\0';
(void) SetImageAttribute(image,"comment",comment);
MagickFreeMemory(comment);
continue;
}
} while (!isdigit(c));
if (base == 2)
return(c-'0');
/*
Evaluate number.
*/
value=0;
do
{
value*=10;
value+=c-'0';
c=ReadBlobByte(image);
if (c == EOF)
return(value);
}
while (isdigit(c));
return(value);
}
#define ValidateScalingIndex(image, index, max) \
do \
{ \
if (index > max) \
ThrowReaderException(CorruptImageError,CorruptImage, image); \
} while (0)
#define ValidateScalingPixel(image, pixel, max) \
do \
{ \
ValidateScalingIndex(image, pixel.red, max); \
ValidateScalingIndex(image, pixel.green, max); \
ValidateScalingIndex(image, pixel.blue, max); \
} while (0)
static Image *ReadPNMImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
char
format;
Image
*image;
long
y;
LongPixelPacket
pixel;
register IndexPacket
*indexes;
register unsigned long
i;
size_t
count,
number_pixels;
unsigned int
index,
raw_sample_bits;
MagickPassFail
status;
unsigned int
max_value,
packets;
Quantum
*scale;
/*
Open image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickSignature);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickSignature);
image=AllocateImage(image_info);
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
if (status == False)
ThrowReaderException(FileOpenError,UnableToOpenFile,image);
/*
Read PNM image.
*/
count=ReadBlob(image,1,(char *) &format);
do
{
/*
Initialize image structure.
*/
if ((count == 0) || (format != 'P'))
ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
format=ReadBlobByte(image);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"PNM Format Id: P%c",
format);
if (format == '7')
(void) PNMInteger(image,10);
image->columns=PNMInteger(image,10);
image->rows=PNMInteger(image,10);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"Dimensions: %lux%lu",
image->columns,image->rows);
if ((format == '1') || (format == '4'))
max_value=1; /* bitmap */
else
max_value=PNMInteger(image,10);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"Max Value: %u",
max_value);
if (max_value <= 1)
image->depth=1;
else if (max_value <= 255U)
image->depth=8;
else if (max_value <= 65535U)
image->depth=16;
else if (max_value <= 4294967295U)
image->depth=32;
raw_sample_bits=image->depth;
image->depth=Min(image->depth,QuantumDepth);
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"Image Depth: %u",
image->depth);
if ((format != '3') && (format != '6'))
{
image->storage_class=PseudoClass;
image->colors=
max_value >= MaxColormapSize ? MaxColormapSize : max_value+1;
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colors: %u",
image->colors);
}
number_pixels=image->columns*image->rows;
if (number_pixels == 0)
ThrowReaderException(CorruptImageError,NegativeOrZeroImageSize,image);
scale=(Quantum *) NULL;
if (image->storage_class == PseudoClass)
{
/*
Create colormap.
*/
if (!AllocateImageColormap(image,image->colors))
ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
image);
if ((format == '7') && (image->colors == 256))
{
/*
Initialize 332 colormap.
*/
i=0;
for (pixel.red=0; pixel.red < 8; pixel.red++)
for (pixel.green=0; pixel.green < 8; pixel.green++)
for (pixel.blue=0; pixel.blue < 4; pixel.blue++)
{
image->colormap[i].red=(Quantum)
(((double) MaxRGB*pixel.red)/0x07+0.5);
image->colormap[i].green=(Quantum)
(((double) MaxRGB*pixel.green)/0x07+0.5);
image->colormap[i].blue=(Quantum)
(((double) MaxRGB*pixel.blue)/0x03+0.5);
i++;
}
}
}
if ((image->storage_class != PseudoClass) || (max_value > MaxRGB))
{
/*
Compute pixel scaling table.
*/
scale=MagickAllocateMemory(Quantum *,
(max_value+1)*sizeof(Quantum));
if (scale == (Quantum *) NULL)
ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
image);
for (i=0; i <= max_value; i++)
scale[i]=ScaleAnyToQuantum((unsigned long) i, max_value);
}
if (image_info->ping && (image_info->subrange != 0))
if (image->scene >= (image_info->subimage+image_info->subrange-1))
break;
/*
Convert PNM pixels to runlength-encoded MIFF packets.
*/
switch (format)
{
case '1':
{
/*
Convert PBM image to pixel packets.
*/
register unsigned long
x;
register PixelPacket
*q;
for (y=0; y < (long) image->rows; y++)
{
q=SetImagePixels(image,0,y,image->columns,1);
if (q == (PixelPacket *) NULL)
break;
indexes=AccessMutableIndexes(image);
for (x=0; x < image->columns; x++)
{
index=!PNMInteger(image,2);
if (EOFBlob(image))
break;
VerifyColormapIndex(image,index);
indexes[x]=index;
*q++=image->colormap[index];
}
if (!SyncImagePixels(image))
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,exception,
LoadImageText,image->filename,
image->columns,image->rows))
break;
if (EOFBlob(image))
break;
}
image->is_grayscale=MagickTrue;
image->is_monochrome=MagickTrue;
if (EOFBlob(image))
ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,
image->filename);
break;
}
case '2':
{
/*
Convert PGM image to pixel packets.
*/
register unsigned long
x;
register PixelPacket
*q;
unsigned long
intensity;
MagickBool
is_grayscale,
is_monochrome;
is_grayscale=MagickTrue;
is_monochrome=MagickTrue;
for (y=0; y < (long) image->rows; y++)
{
q=SetImagePixels(image,0,y,image->columns,1);
if (q == (PixelPacket *) NULL)
break;
indexes=AccessMutableIndexes(image);
for (x=0; x < image->columns; x++)
{
intensity=PNMInteger(image,10);
ValidateScalingIndex(image, intensity, max_value);
if (EOFBlob(image))
break;
if (scale != (Quantum *) NULL)
intensity=scale[intensity];
index=intensity;
VerifyColormapIndex(image,index);
indexes[x]=index;
*q=image->colormap[index];
is_monochrome &= IsMonochrome(*q);
q++;
}
if (EOFBlob(image))
break;
if (!SyncImagePixels(image))
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,exception,
LoadImageText,image->filename,
image->columns,image->rows))
break;
}
image->is_monochrome=is_monochrome;
image->is_grayscale=is_grayscale;
if (EOFBlob(image))
ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,
image->filename);
break;
}
case '3':
{
/*
Convert PNM image to pixel packets.
*/
register unsigned long
x;
register PixelPacket
*q;
MagickBool
is_grayscale,
is_monochrome;
is_grayscale=MagickTrue;
is_monochrome=MagickTrue;
for (y=0; y < (long) image->rows; y++)
{
q=SetImagePixels(image,0,y,image->columns,1);
if (q == (PixelPacket *) NULL)
break;
for (x=0; x < image->columns; x++)
{
pixel.red=PNMInteger(image,10);
pixel.green=PNMInteger(image,10);
pixel.blue=PNMInteger(image,10);
if (EOFBlob(image))
break;
ValidateScalingPixel(image, pixel, max_value);
if (scale != (Quantum *) NULL)
{
pixel.red=scale[pixel.red];
pixel.green=scale[pixel.green];
pixel.blue=scale[pixel.blue];
}
q->red=(Quantum) pixel.red;
q->green=(Quantum) pixel.green;
q->blue=(Quantum) pixel.blue;
is_monochrome &= IsMonochrome(*q);
is_grayscale &= IsGray(*q);
q++;
}
if (EOFBlob(image))
break;
if (!SyncImagePixels(image))
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,exception,
LoadImageText,image->filename,
image->columns,image->rows))
break;
}
image->is_monochrome=is_monochrome;
image->is_grayscale=is_grayscale;
if (EOFBlob(image))
ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,
image->filename);
break;
}
case '4':
{
ImportPixelAreaOptions
import_options;
size_t
bytes_per_row;
unsigned long
row_count=0;
ThreadViewDataSet
*scanline_set;
/*
Convert PBM raw image to pixel packets.
*/
bytes_per_row=((image->columns+7) >> 3);
scanline_set=AllocateThreadViewDataArray(image,exception,bytes_per_row,1);
if (scanline_set == (ThreadViewDataSet *) NULL)
ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
ImportPixelAreaOptionsInit(&import_options);
import_options.grayscale_miniswhite=MagickTrue;
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
# pragma omp parallel for schedule(static,1) shared(row_count,status)
#endif
for (y=0; y < (long) image->rows; y++)
{
void
*pixels;
MagickBool
thread_status;
unsigned long
thread_row_count;
ImportPixelAreaInfo
import_info;
thread_status=status;
if (thread_status == MagickFail)
continue;
pixels=AccessThreadViewData(scanline_set);
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
# pragma omp critical (GM_ReadPNMImage)
#endif
{
if (ReadBlobZC(image,bytes_per_row,&pixels) != bytes_per_row)
thread_status=MagickFail;
thread_row_count=row_count;
row_count++;
if (image->previous == (Image *) NULL)
if (QuantumTick(thread_row_count,image->rows))
if (!MagickMonitorFormatted(thread_row_count,image->rows,
exception,LoadImageText,
image->filename,
image->columns,image->rows))
thread_status=MagickFail;
}
if (thread_status != MagickFail)
if (SetImagePixels(image,0,thread_row_count,image->columns,1) ==
(PixelPacket *) NULL)
thread_status=MagickFail;
if (thread_status != MagickFail)
if (!ImportImagePixelArea(image,GrayQuantum,1,pixels,
&import_options,&import_info))
thread_status=MagickFail;
if (thread_status != MagickFail)
if (!SyncImagePixels(image))
thread_status=MagickFail;
if (thread_status == MagickFail)
{
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
# pragma omp critical (GM_ReadPNMImage)
#endif
status=MagickFail;
}
}
image->is_grayscale=MagickTrue;
image->is_monochrome=MagickTrue;
DestroyThreadViewDataSet(scanline_set);
if (EOFBlob(image))
ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,
image->filename);
break;
}
case '5':
case '7':
{
/*
Convert PGM raw image to pixel packets.
*/
size_t
bytes_per_row;
MagickBool
is_monochrome=MagickTrue;
unsigned long
row_count=0;
ThreadViewDataSet
*scanline_set;
packets=(raw_sample_bits+7)/8;
bytes_per_row=packets*image->columns;
scanline_set=AllocateThreadViewDataArray(image,exception,bytes_per_row,1);
if (scanline_set == (ThreadViewDataSet *) NULL)
ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
# pragma omp parallel for schedule(static,1) shared(is_monochrome,row_count,status)
#endif
for (y=0; y < (long) image->rows; y++)
{
register unsigned long
x;
register PixelPacket
*q;
void
*pixels;
MagickBool
thread_status;
MagickBool
thread_is_monochrome;
unsigned long
thread_row_count;
thread_status=status;
if (thread_status == MagickFail)
continue;
pixels=AccessThreadViewData(scanline_set);
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
# pragma omp critical (GM_ReadPNMImage)
#endif
{
thread_is_monochrome=is_monochrome;
if (ReadBlobZC(image,bytes_per_row,&pixels) != bytes_per_row)
thread_status=MagickFail;
thread_row_count=row_count;
row_count++;
if (image->previous == (Image *) NULL)
if (QuantumTick(thread_row_count,image->rows))
if (!MagickMonitorFormatted(thread_row_count,image->rows,
exception,LoadImageText,
image->filename,
image->columns,image->rows))
thread_status=MagickFail;
}
if (thread_status != MagickFail)
if ((q=SetImagePixels(image,0,thread_row_count,
image->columns,1)) == (PixelPacket *) NULL)
thread_status=MagickFail;
if (thread_status != MagickFail)
if (!ImportImagePixelArea(image,GrayQuantum,raw_sample_bits,
pixels,0,0))
thread_status=MagickFail;
/*
Check all pixels for gray/monochrome status since this
format is often used for input from Ghostscript, which may
output bilevel in a gray format. It is easier to check
now while the pixels are still "hot" in memory.
*/
if (thread_status != MagickFail)
if (thread_is_monochrome)
for (x=image->columns; x; x--)
{
thread_is_monochrome = (thread_is_monochrome &&
IsMonochrome(*q));
if (!thread_is_monochrome)
break;
q++;
}
if (thread_status != MagickFail)
if (!SyncImagePixels(image))
thread_status=MagickFail;
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
# pragma omp critical (GM_ReadPNMImage)
#endif
{
if (thread_status == MagickFail)
status=MagickFail;
if (!thread_is_monochrome)
is_monochrome=thread_is_monochrome;
}
}
DestroyThreadViewDataSet(scanline_set);
image->is_grayscale=MagickTrue;
image->is_monochrome=is_monochrome;
if (EOFBlob(image))
ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,
image->filename);
break;
}
case '6':
{
/*
Convert PPM raw raster image to pixel packets.
*/
size_t
bytes_per_row;
MagickBool
is_grayscale,
is_monochrome;
unsigned long
row_count=0;
ThreadViewDataSet
*scanline_set;
is_grayscale=MagickTrue;
is_monochrome=MagickTrue;
packets=((raw_sample_bits+7)/8)*3;
bytes_per_row=packets*image->columns;
scanline_set=AllocateThreadViewDataArray(image,exception,bytes_per_row,1);
if (scanline_set == (ThreadViewDataSet *) NULL)
ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
#if 1
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
# pragma omp parallel for schedule(static,1) shared(is_grayscale,is_monochrome,row_count,status)
#endif
#endif
for (y=0; y < (long) image->rows; y++)
{
register unsigned long
x;
register PixelPacket
*q;
void
*pixels;
MagickBool
thread_status;
MagickBool
thread_is_grayscale,
thread_is_monochrome;
unsigned long
thread_row_count;
thread_status=status;
if (thread_status == MagickFail)
continue;
pixels=AccessThreadViewData(scanline_set);
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
# pragma omp critical (GM_ReadPNMImage)
#endif
{
thread_is_grayscale=is_grayscale;
thread_is_monochrome=is_monochrome;
if (ReadBlobZC(image,bytes_per_row,&pixels) != bytes_per_row)
thread_status=MagickFail;
thread_row_count=row_count;
row_count++;
if (image->previous == (Image *) NULL)
if (QuantumTick(thread_row_count,image->rows))
if (!MagickMonitorFormatted(thread_row_count,image->rows,
exception,LoadImageText,
image->filename,
image->columns,image->rows))
thread_status=MagickFail;
}
if (thread_status != MagickFail)
if ((q=SetImagePixels(image,0,thread_row_count,image->columns,1)) ==
(PixelPacket *) NULL)
thread_status=MagickFail;
if (thread_status != MagickFail)
if (!ImportImagePixelArea(image,RGBQuantum,raw_sample_bits,pixels,0,0))
thread_status=MagickFail;
/*
Check all pixels for gray/monochrome status since this
format is often used for input from Ghostscript, which may
output bilevel or gray in an RGB format. It is easier to
check now while the pixels are still "hot" in memory.
*/
if (thread_status != MagickFail)
if (thread_is_grayscale || thread_is_monochrome)
for (x=image->columns; x; x--)
{
thread_is_grayscale = thread_is_grayscale && IsGray(*q);
thread_is_monochrome = thread_is_monochrome && IsMonochrome(*q);
if (!thread_is_grayscale && !thread_is_monochrome)
break;
q++;
}
if (thread_status != MagickFail)
if (!SyncImagePixels(image))
thread_status=MagickFail;
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
# pragma omp critical (GM_ReadPNMImage)
#endif
{
if (thread_status == MagickFail)
status=MagickFail;
if (!thread_is_grayscale)
is_grayscale=thread_is_grayscale;
if (!thread_is_monochrome)
is_monochrome=thread_is_monochrome;
}
}
DestroyThreadViewDataSet(scanline_set);
image->is_monochrome=is_monochrome;
image->is_grayscale=is_grayscale;
if (EOFBlob(image))
ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,
image->filename);
break;
}
default:
ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
}
if (scale != (Quantum *) NULL)
MagickFreeMemory(scale);
/*
Proceed to next image.
*/
if (image_info->subrange != 0)
if (image->scene >= (image_info->subimage+image_info->subrange-1))
break;
if ((format == '1') || (format == '2') || (format == '3'))
do
{
/*
Skip to end of line.
*/
count=ReadBlob(image,1,&format);
if (count == 0)
break;
} while (format != '\n');
count=ReadBlob(image,1,(char *) &format);
if ((count != 0) && (format == 'P'))
{
/*
Allocate next image structure.
*/
AllocateNextImage(image_info,image);
if (image->next == (Image *) NULL)
{
DestroyImageList(image);
return((Image *) NULL);
}
image=SyncNextImageInList(image);
if (!MagickMonitorFormatted(TellBlob(image),GetBlobSize(image),
exception,LoadImagesText,
image->filename))
break;
}
} while ((count != 0) && (format == 'P'));
while (image->previous != (Image *) NULL)
image=image->previous;
CloseBlob(image);
return(image);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e g i s t e r P N M I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Method RegisterPNMImage adds attributes for the PNM 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 RegisterPNMImage method is:
%
% RegisterPNMImage(void)
%
*/
ModuleExport void RegisterPNMImage(void)
{
MagickInfo
*entry;
entry=SetMagickInfo("P7");
entry->decoder=(DecoderHandler) ReadPNMImage;
entry->encoder=(EncoderHandler) WritePNMImage;
entry->description="Xv thumbnail format";
entry->module="PNM";
(void) RegisterMagickInfo(entry);
entry=SetMagickInfo("PBM");
entry->decoder=(DecoderHandler) ReadPNMImage;
entry->encoder=(EncoderHandler) WritePNMImage;
entry->description="Portable bitmap format (black/white)";
entry->module="PNM";
entry->coder_class=PrimaryCoderClass;
(void) RegisterMagickInfo(entry);
entry=SetMagickInfo("PGM");
entry->decoder=(DecoderHandler) ReadPNMImage;
entry->encoder=(EncoderHandler) WritePNMImage;
entry->description="Portable graymap format (gray scale)";
entry->module="PNM";
entry->coder_class=PrimaryCoderClass;
(void) RegisterMagickInfo(entry);
entry=SetMagickInfo("PNM");
entry->decoder=(DecoderHandler) ReadPNMImage;
entry->encoder=(EncoderHandler) WritePNMImage;
entry->magick=(MagickHandler) IsPNM;
entry->description="Portable anymap";
entry->module="PNM";
entry->coder_class=PrimaryCoderClass;
(void) RegisterMagickInfo(entry);
entry=SetMagickInfo("PPM");
entry->decoder=(DecoderHandler) ReadPNMImage;
entry->encoder=(EncoderHandler) WritePNMImage;
entry->description="Portable pixmap format (color)";
entry->module="PNM";
entry->coder_class=PrimaryCoderClass;
(void) RegisterMagickInfo(entry);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% U n r e g i s t e r P N M I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Method UnregisterPNMImage removes format registrations made by the
% PNM module from the list of supported formats.
%
% The format of the UnregisterPNMImage method is:
%
% UnregisterPNMImage(void)
%
*/
ModuleExport void UnregisterPNMImage(void)
{
(void) UnregisterMagickInfo("P7");
(void) UnregisterMagickInfo("PBM");
(void) UnregisterMagickInfo("PGM");
(void) UnregisterMagickInfo("PNM");
(void) UnregisterMagickInfo("PPM");
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% W r i t e P N M I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Procedure WritePNMImage writes an image to a file in the PNM rasterfile
% format.
%
% The format of the WritePNMImage method is:
%
% unsigned int WritePNMImage(const ImageInfo *image_info,Image *image)
%
% A description of each parameter follows.
%
% o status: Method WritePNMImage return True if the image is written.
% False is returned is there is a memory shortage or if the image file
% fails to write.
%
% o image_info: Specifies a pointer to a ImageInfo structure.
%
% o image: A pointer to an Image structure.
%
%
*/
static unsigned int WritePNMImage(const ImageInfo *image_info,Image *image)
{
char
buffer[MaxTextExtent];
const ImageAttribute
*attribute;
IndexPacket
index;
int
format;
unsigned int
depth;
unsigned long
j,
y;
register const PixelPacket
*p;
register const IndexPacket
*indexes;
register unsigned long
i,
x;
unsigned int
scene,
status;
/*
Open output image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickSignature);
assert(image != (Image *) NULL);
assert(image->signature == MagickSignature);
status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
if (status == False)
ThrowWriterException(FileOpenError,UnableToOpenFile,image);
scene=0;
do
{
ImageCharacteristics
characteristics;
/*
Make sure that image is in an RGB type space.
*/
(void) TransformColorspace(image,RGBColorspace);
/*
Analyze image to be written.
*/
if (!GetImageCharacteristics(image,&characteristics,
(OptimizeType == image_info->type),
&image->exception))
{
CloseBlob(image);
return MagickFail;
}
depth=(image->depth <= 8 ? 8 : 16);
/*
Write PNM file header.
*/
format=6;
if (LocaleCompare(image_info->magick,"PGM") == 0)
{
format=5;
}
else if (LocaleCompare(image_info->magick,"PBM") == 0)
{
format=4;
}
else if (LocaleCompare(image_info->magick,"PNM") == 0)
{
if ((characteristics.monochrome) &&
(image_info->type != GrayscaleType) &&
(image_info->type != GrayscaleMatteType) &&
(image_info->type != TrueColorType) &&
(image_info->type != TrueColorMatteType))
{
/* PBM */
format=4;
}
else if ((characteristics.grayscale) &&
(image_info->type != TrueColorType) &&
(image_info->type != TrueColorMatteType))
{
/* PGM */
format=5;
}
else
{
/* PPM */
format=6;
}
}
/*
If quality is set to zero, then select an ASCII subformat.
*/
if (image_info->quality == 0)
{
format-=3;
}
if (LocaleCompare(image_info->magick,"P7") != 0)
{
FormatString(buffer,"P%d\n",format);
}
else
{
format=7;
(void) strcpy(buffer,"P7 332\n");
}
(void) WriteBlobString(image,buffer);
attribute=GetImageAttribute(image,"comment");
if (attribute != (const ImageAttribute *) NULL)
{
register char
*av;
/*
Write comments to file.
*/
(void) WriteBlobByte(image,'#');
for (av=attribute->value; *av != '\0'; av++)
{
(void) WriteBlobByte(image,*av);
if ((*av == '\n') && (*(av+1) != '\0'))
(void) WriteBlobByte(image,'#');
}
(void) WriteBlobByte(image,'\n');
}
if (format != 7)
{
FormatString(buffer,"%lu %lu\n",image->columns,image->rows);
(void) WriteBlobString(image,buffer);
}
/*
Convert runlength encoded to PNM raster pixels.
*/
(void) LogMagickEvent(CoderEvent,GetMagickModule(),"PNM Format Id: P%d",
format);
switch (format)
{
case 1:
{
unsigned int
polarity;
/*
Convert image to a PBM image.
*/
(void) SetImageType(image,BilevelType);
polarity=PixelIntensityToQuantum(&image->colormap[0]) < (MaxRGB/2);
if (image->colors == 2)
polarity=PixelIntensityToQuantum(&image->colormap[0]) <
PixelIntensityToQuantum(&image->colormap[1]);
i=0;
for (y=0; y < image->rows; y++)
{
p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
if (p == (const PixelPacket *) NULL)
break;
indexes=AccessImmutableIndexes(image);
for (x=0; x < image->columns; x++)
{
FormatString(buffer,"%u ",indexes[x] == polarity ? 0x00 : 0x01);
(void) WriteBlobString(image,buffer);
i++;
if (i == 36)
{
(void) WriteBlobByte(image,'\n');
i=0;
}
}
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,&image->exception,
SaveImageText,image->filename,
image->columns,image->rows))
break;
}
if (i != 0)
(void) WriteBlobByte(image,'\n');
break;
}
case 2:
{
/*
Convert image to a PGM image.
*/
if (depth <= 8)
(void) WriteBlobString(image,"255\n");
else
(void) WriteBlobString(image,"65535\n");
i=0;
for (y=0; y < image->rows; y++)
{
p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
if (p == (const PixelPacket *) NULL)
break;
for (x=0; x < image->columns; x++)
{
index=PixelIntensityToQuantum(p);
if (depth <= 8)
FormatString(buffer," %u",ScaleQuantumToChar(index));
else
FormatString(buffer," %u",ScaleQuantumToShort(index));
(void) WriteBlobString(image,buffer);
i++;
if (i == 12)
{
(void) WriteBlobByte(image,'\n');
i=0;
}
p++;
}
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,&image->exception,
SaveImageText,image->filename,
image->columns,image->rows))
break;
}
if (i != 0)
(void) WriteBlobByte(image,'\n');
break;
}
case 3:
{
/*
Convert image to a PNM image.
*/
if (depth <= 8)
(void) WriteBlobString(image,"255\n");
else
(void) WriteBlobString(image,"65535\n");
i=0;
for (y=0; y < image->rows; y++)
{
p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
if (p == (const PixelPacket *) NULL)
break;
for (x=0; x < image->columns; x++)
{
if (depth <= 8)
FormatString(buffer,"%u %u %u ",ScaleQuantumToChar(p->red),
ScaleQuantumToChar(p->green),ScaleQuantumToChar(p->blue));
else
FormatString(buffer,"%u %u %u ",ScaleQuantumToShort(p->red),
ScaleQuantumToShort(p->green),ScaleQuantumToShort(p->blue));
(void) WriteBlobString(image,buffer);
i++;
if (i == 4)
{
(void) WriteBlobByte(image,'\n');
i=0;
}
p++;
}
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,&image->exception,
SaveImageText,image->filename,
image->columns,image->rows))
break;
}
if (i != 0)
(void) WriteBlobByte(image,'\n');
break;
}
case 4:
{
/*
Convert image to a PBM image.
*/
size_t
octets;
unsigned char
*pixels;
ExportPixelAreaOptions
export_options;
/*
Allocate memory for pixels.
*/
octets=((image->columns/8)+(image->columns%8 ? 1 : 0));
pixels=MagickAllocateMemory(unsigned char *,octets);
if (pixels == (unsigned char *) NULL)
ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,
image);
ExportPixelAreaOptionsInit(&export_options);
export_options.grayscale_miniswhite=MagickTrue;
for (y=0; y < image->rows; y++)
{
p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
if (p == (const PixelPacket *) NULL)
break;
if (!ExportImagePixelArea(image,GrayQuantum,1,pixels,
&export_options,0))
break;
if (WriteBlob(image,octets,(char *) pixels) != octets)
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,&image->exception,
SaveImageText,image->filename,
image->columns,image->rows))
break;
}
MagickFreeMemory(pixels);
break;
}
case 5:
{
/*
Convert image to a PGM image.
*/
size_t
octets;
unsigned char
*pixels;
/*
Allocate memory for pixels.
*/
octets=(depth <= 8 ? 1 : 2)*image->columns;
pixels=MagickAllocateMemory(unsigned char *,octets);
if (pixels == (unsigned char *) NULL)
ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,
image);
if (depth <= 8)
(void) WriteBlobString(image,"255\n");
else
(void) WriteBlobString(image,"65535\n");
for (y=0; y < image->rows; y++)
{
p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
if (p == (const PixelPacket *) NULL)
break;
if (!ExportImagePixelArea(image,GrayQuantum,depth,pixels,0,0))
break;
if (WriteBlob(image,octets,(char *) pixels) != octets)
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,&image->exception,
SaveImageText,image->filename,
image->columns,image->rows))
break;
}
MagickFreeMemory(pixels);
break;
}
case 6:
{
/*
Convert image to a PPM image.
*/
size_t
octets;
unsigned char
*pixels;
/*
Allocate memory for pixels.
*/
octets=(depth <= 8 ? 3 : 6)*image->columns;
pixels=MagickAllocateMemory(unsigned char *,octets);
if (pixels == (unsigned char *) NULL)
ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,
image);
/*
Convert image to a PNM image.
*/
if (depth <= 8)
(void) WriteBlobString(image,"255\n");
else
(void) WriteBlobString(image,"65535\n");
for (y=0; y < image->rows; y++)
{
p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
if (p == (const PixelPacket *) NULL)
break;
if (ExportImagePixelArea(image,RGBQuantum,depth,pixels,0,0) == MagickFail)
break;
if (WriteBlob(image,octets,(char *) pixels) != octets)
break;
if (image->previous == (Image *) NULL)
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,&image->exception,
SaveImageText,image->filename,
image->columns,image->rows))
break;
}
MagickFreeMemory(pixels);
break;
}
case 7:
{
static const short int
dither_red[2][16]=
{
{-16, 4, -1, 11,-14, 6, -3, 9,-15, 5, -2, 10,-13, 7, -4, 8},
{ 15, -5, 0,-12, 13, -7, 2,-10, 14, -6, 1,-11, 12, -8, 3, -9}
},
dither_green[2][16]=
{
{ 11,-15, 7, -3, 8,-14, 4, -2, 10,-16, 6, -4, 9,-13, 5, -1},
{-12, 14, -8, 2, -9, 13, -5, 1,-11, 15, -7, 3,-10, 12, -6, 0}
},
dither_blue[2][16]=
{
{ -3, 9,-13, 7, -1, 11,-15, 5, -4, 8,-14, 6, -2, 10,-16, 4},
{ 2,-10, 12, -8, 0,-12, 14, -6, 3, -9, 13, -7, 1,-11, 15, -5}
};
long
value;
Quantum
pixel;
unsigned short
*blue_map[2][16],
*green_map[2][16],
*red_map[2][16];
/*
Allocate and initialize dither maps.
*/
for (i=0; i < 2; i++)
for (j=0; j < 16; j++)
{
red_map[i][j]=MagickAllocateMemory(unsigned short *,
256*sizeof(unsigned short));
green_map[i][j]=MagickAllocateMemory(unsigned short *,
256*sizeof(unsigned short));
blue_map[i][j]=MagickAllocateMemory(unsigned short *,
256*sizeof(unsigned short));
if ((red_map[i][j] == (unsigned short *) NULL) ||
(green_map[i][j] == (unsigned short *) NULL) ||
(blue_map[i][j] == (unsigned short *) NULL))
ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,
image);
}
/*
Initialize dither tables.
*/
for (i=0; i < 2; i++)
for (j=0; j < 16; j++)
for (x=0; x < 256; x++)
{
value=x-16;
if (x < 48)
value=x/2+8;
value+=dither_red[i][j];
red_map[i][j][x]=(unsigned short)
((value < 0) ? 0 : (value > 255) ? 255 : value);
value=x-16;
if (x < 48)
value=x/2+8;
value+=dither_green[i][j];
green_map[i][j][x]=(unsigned short)
((value < 0) ? 0 : (value > 255) ? 255 : value);
value=x-32;
if (x < 112)
value=x/2+24;
value+=2*dither_blue[i][j];
blue_map[i][j][x]=(unsigned short)
((value < 0) ? 0 : (value > 255) ? 255 : value);
}
/*
Convert image to a P7 image.
*/
(void) WriteBlobString(image,"#END_OF_COMMENTS\n");
FormatString(buffer,"%lu %lu 255\n",image->columns,image->rows);
(void) WriteBlobString(image,buffer);
i=0;
j=0;
for (y=0; y < image->rows; y++)
{
p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
if (p == (const PixelPacket *) NULL)
break;
for (x=0; x < image->columns; x++)
{
if (!image_info->dither)
pixel=(Quantum) ((ScaleQuantumToChar(p->red) & 0xe0) |
((ScaleQuantumToChar(p->green) & 0xe0) >> 3) |
((ScaleQuantumToChar(p->blue) & 0xc0) >> 6));
else
pixel=(Quantum)
((red_map[i][j][ScaleQuantumToChar(p->red)] & 0xe0) |
((green_map[i][j][ScaleQuantumToChar(p->green)] & 0xe0) >> 3) |
((blue_map[i][j][ScaleQuantumToChar(p->blue)] & 0xc0) >> 6));
(void) WriteBlobByte(image,pixel);
p++;
j++;
if (j == 16)
j=0;
}
i++;
if (i == 2)
i=0;
if (QuantumTick(y,image->rows))
if (!MagickMonitorFormatted(y,image->rows,&image->exception,
SaveImageText,image->filename,
image->columns,image->rows))
break;
}
/*
Free allocated memory.
*/
for (i=0; i < 2; i++)
for (j=0; j < 16; j++)
{
MagickFreeMemory(green_map[i][j]);
MagickFreeMemory(blue_map[i][j]);
MagickFreeMemory(red_map[i][j]);
}
break;
}
}
if (image->next == (Image *) NULL)
break;
image=SyncNextImageInList(image);
status=MagickMonitorFormatted(scene++,GetImageListLength(image),
&image->exception,SaveImagesText,
image->filename);
if (status == False)
break;
} while (image_info->adjoin);
if (image_info->adjoin)
while (image->previous != (Image *) NULL)
image=image->previous;
CloseBlob(image);
return(True);
}