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

1190 lines
37 KiB

This file contains invisible Unicode characters!

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

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% PPPP CCCC DDDD %
% P P C D D %
% PPPP C D D %
% P C D D %
% P CCCC DDDD %
% %
% %
% Read/Write Photo CD Image Format %
% %
% Software Design %
% Cristy %
% July 1992 %
% %
% %
% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
% obtain a copy of the License at %
% %
% https://imagemagick.org/script/license.php %
% %
% Unless required by applicable law or agreed to in writing, software %
% distributed under the License is distributed on an "AS IS" BASIS, %
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
% See the License for the specific language governing permissions and %
% limitations under the License. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/
/*
Include declarations.
*/
#include "MagickCore/studio.h"
#include "MagickCore/property.h"
#include "MagickCore/blob.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache.h"
#include "MagickCore/client.h"
#include "MagickCore/colorspace.h"
#include "MagickCore/colorspace-private.h"
#include "MagickCore/constitute.h"
#include "MagickCore/decorate.h"
#include "MagickCore/distort.h"
#include "MagickCore/exception.h"
#include "MagickCore/exception-private.h"
#include "MagickCore/gem.h"
#include "MagickCore/geometry.h"
#include "MagickCore/image.h"
#include "MagickCore/image-private.h"
#include "MagickCore/list.h"
#include "MagickCore/magick.h"
#include "MagickCore/memory_.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/montage.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/resize.h"
#include "MagickCore/resource_.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/static.h"
#include "MagickCore/string_.h"
#include "MagickCore/module.h"
#include "MagickCore/utility.h"
/*
Forward declarations.
*/
static MagickBooleanType
WritePCDImage(const ImageInfo *,Image *,ExceptionInfo *);
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% D e c o d e I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% DecodeImage recovers the Huffman encoded luminance and chrominance
% deltas.
%
% The format of the DecodeImage method is:
%
% MagickBooleanType DecodeImage(Image *image,unsigned char *luma,
% unsigned char *chroma1,unsigned char *chroma2)
%
% A description of each parameter follows:
%
% o image: the address of a structure of type Image.
%
% o luma: the address of a character buffer that contains the
% luminance information.
%
% o chroma1: the address of a character buffer that contains the
% chrominance information.
%
% o chroma2: the address of a character buffer that contains the
% chrominance information.
%
*/
static MagickBooleanType DecodeImage(Image *image,unsigned char *luma,
unsigned char *chroma1,unsigned char *chroma2,ExceptionInfo *exception)
{
#define IsSync(sum) ((sum & 0xffffff00UL) == 0xfffffe00UL)
#define PCDGetBits(n) \
{ \
sum=(sum << n) & 0xffffffff; \
bits-=n; \
while (bits <= 24) \
{ \
if (p >= (buffer+0x800)) \
{ \
count=ReadBlob(image,0x800,buffer); \
p=buffer; \
} \
sum|=(((unsigned int) (*p)) << (24-bits)); \
bits+=8; \
p++; \
} \
}
typedef struct PCDTable
{
unsigned int
length,
sequence;
MagickStatusType
mask;
unsigned char
key;
} PCDTable;
PCDTable
*pcd_table[3];
ssize_t
i,
j;
PCDTable
*r;
unsigned char
*p,
*q;
size_t
bits,
length,
plane,
pcd_length[3],
row,
sum;
ssize_t
count,
quantum;
unsigned char
*buffer;
/*
Initialize Huffman tables.
*/
assert(image != (const Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
assert(luma != (unsigned char *) NULL);
assert(chroma1 != (unsigned char *) NULL);
assert(chroma2 != (unsigned char *) NULL);
buffer=(unsigned char *) AcquireQuantumMemory(0x800,sizeof(*buffer));
if (buffer == (unsigned char *) NULL)
ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
image->filename);
sum=0;
bits=32;
p=buffer+0x800;
for (i=0; i < 3; i++)
{
pcd_table[i]=(PCDTable *) NULL;
pcd_length[i]=0;
}
for (i=0; i < (ssize_t) (image->columns > 1536 ? 3 : 1); i++)
{
PCDGetBits(8);
length=(sum & 0xff)+1;
pcd_table[i]=(PCDTable *) AcquireQuantumMemory(length,
sizeof(*pcd_table[i]));
if (pcd_table[i] == (PCDTable *) NULL)
{
buffer=(unsigned char *) RelinquishMagickMemory(buffer);
for (j=0; j < i; j++)
pcd_table[j]=(PCDTable *) RelinquishMagickMemory(pcd_table[j]);
ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
image->filename);
}
r=pcd_table[i];
for (j=0; j < (ssize_t) length; j++)
{
PCDGetBits(8);
r->length=(unsigned int) (sum & 0xff)+1;
if (r->length > 16)
{
buffer=(unsigned char *) RelinquishMagickMemory(buffer);
for (j=0; j <= i; j++)
pcd_table[j]=(PCDTable *) RelinquishMagickMemory(pcd_table[j]);
return(MagickFalse);
}
PCDGetBits(16);
r->sequence=(unsigned int) (sum & 0xffff) << 16;
PCDGetBits(8);
r->key=(unsigned char) (sum & 0xff);
r->mask=(~((1U << (32-r->length))-1));
r++;
}
pcd_length[i]=(size_t) length;
}
if (EOFBlob(image) == MagickFalse)
{
/*
Search for Sync byte.
*/
for (i=0; i < 1; i++)
PCDGetBits(16);
for (i=0; i < 1; i++)
PCDGetBits(16);
while ((sum & 0x00fff000UL) != 0x00fff000UL)
PCDGetBits(8);
while (IsSync(sum) == 0)
PCDGetBits(1);
}
/*
Recover the Huffman encoded luminance and chrominance deltas.
*/
count=0;
length=0;
plane=0;
row=0;
for (q=luma; EOFBlob(image) == MagickFalse; )
{
if (IsSync(sum) != 0)
{
/*
Determine plane and row number.
*/
PCDGetBits(16);
row=((sum >> 9) & 0x1fff);
if (row == image->rows)
break;
PCDGetBits(8);
plane=sum >> 30;
PCDGetBits(16);
switch (plane)
{
case 0:
{
q=luma+row*image->columns;
count=(ssize_t) image->columns;
break;
}
case 2:
{
q=chroma1+(row >> 1)*image->columns;
count=(ssize_t) (image->columns >> 1);
plane--;
break;
}
case 3:
{
q=chroma2+(row >> 1)*image->columns;
count=(ssize_t) (image->columns >> 1);
plane--;
break;
}
default:
{
for (i=0; i < (ssize_t) (image->columns > 1536 ? 3 : 1); i++)
pcd_table[i]=(PCDTable *) RelinquishMagickMemory(pcd_table[i]);
buffer=(unsigned char *) RelinquishMagickMemory(buffer);
ThrowBinaryException(CorruptImageError,"CorruptImage",
image->filename);
}
}
length=pcd_length[plane];
continue;
}
/*
Decode luminance or chrominance deltas.
*/
r=pcd_table[plane];
for (i=0; ((i < (ssize_t) length) && ((sum & r->mask) != r->sequence)); i++)
r++;
if ((row > image->rows) || (r == (PCDTable *) NULL))
{
(void) ThrowMagickException(exception,GetMagickModule(),
CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
while ((sum & 0x00fff000) != 0x00fff000)
PCDGetBits(8);
while (IsSync(sum) == 0)
PCDGetBits(1);
continue;
}
if (r->key < 128)
quantum=(ssize_t) (*q)+r->key;
else
quantum=(ssize_t) (*q)+r->key-256;
*q=(unsigned char) ((quantum < 0) ? 0 : (quantum > 255) ? 255 : quantum);
q++;
PCDGetBits(r->length);
count--;
}
/*
Relinquish resources.
*/
for (i=0; i < (ssize_t) (image->columns > 1536 ? 3 : 1); i++)
pcd_table[i]=(PCDTable *) RelinquishMagickMemory(pcd_table[i]);
buffer=(unsigned char *) RelinquishMagickMemory(buffer);
return(MagickTrue);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I s P C D %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% IsPCD() returns MagickTrue if the image format type, identified by the
% magick string, is PCD.
%
% The format of the IsPCD method is:
%
% MagickBooleanType IsPCD(const unsigned char *magick,const size_t length)
%
% A description of each parameter follows:
%
% o magick: compare image format pattern against these bytes.
%
% o length: Specifies the length of the magick string.
%
*/
static MagickBooleanType IsPCD(const unsigned char *magick,const size_t length)
{
if (length < 2052)
return(MagickFalse);
if (LocaleNCompare((const char *) magick+2048,"PCD_",4) == 0)
return(MagickTrue);
return(MagickFalse);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e a d P C D I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ReadPCDImage() reads a Photo CD image file and returns it. It
% allocates the memory necessary for the new Image structure and returns a
% pointer to the new image. Much of the PCD decoder was derived from
% the program hpcdtoppm(1) by Hadmut Danisch.
%
% The format of the ReadPCDImage method is:
%
% image=ReadPCDImage(image_info)
%
% A description of each parameter follows:
%
% o image_info: the image info.
%
% o exception: return any errors or warnings in this structure.
%
*/
static Image *OverviewImage(const ImageInfo *image_info,Image *image,
ExceptionInfo *exception)
{
Image
*montage_image;
MontageInfo
*montage_info;
Image
*p;
/*
Create the PCD Overview image.
*/
for (p=image; p != (Image *) NULL; p=p->next)
{
(void) DeleteImageProperty(p,"label");
(void) SetImageProperty(p,"label",DefaultTileLabel,exception);
}
montage_info=CloneMontageInfo(image_info,(MontageInfo *) NULL);
(void) CopyMagickString(montage_info->filename,image_info->filename,
MagickPathExtent);
montage_image=MontageImageList(image_info,montage_info,image,exception);
montage_info=DestroyMontageInfo(montage_info);
if (montage_image == (Image *) NULL)
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
image=DestroyImageList(image);
return(montage_image);
}
static void Upsample(const size_t width,const size_t height,
const size_t scaled_width,unsigned char *pixels)
{
ssize_t
x,
y;
unsigned char
*p,
*q,
*r;
/*
Create a new image that is a integral size greater than an existing one.
*/
assert(pixels != (unsigned char *) NULL);
for (y=0; y < (ssize_t) height; y++)
{
p=pixels+(height-1-y)*scaled_width+(width-1);
q=pixels+((height-1-y) << 1)*scaled_width+((width-1) << 1);
*q=(*p);
*(q+1)=(*(p));
for (x=1; x < (ssize_t) width; x++)
{
p--;
q-=2;
*q=(*p);
*(q+1)=(unsigned char) ((((size_t) *p)+((size_t) *(p+1))+1) >> 1);
}
}
for (y=0; y < (ssize_t) (height-1); y++)
{
p=pixels+((size_t) y << 1)*scaled_width;
q=p+scaled_width;
r=q+scaled_width;
for (x=0; x < (ssize_t) (width-1); x++)
{
*q=(unsigned char) ((((size_t) *p)+((size_t) *r)+1) >> 1);
*(q+1)=(unsigned char) ((((size_t) *p)+((size_t) *(p+2))+
((size_t) *r)+((size_t) *(r+2))+2) >> 2);
q+=2;
p+=2;
r+=2;
}
*q++=(unsigned char) ((((size_t) *p++)+((size_t) *r++)+1) >> 1);
*q++=(unsigned char) ((((size_t) *p++)+((size_t) *r++)+1) >> 1);
}
p=pixels+(2*height-2)*scaled_width;
q=pixels+(2*height-1)*scaled_width;
(void) memcpy(q,p,(size_t) (2*width));
}
static Image *ReadPCDImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
#define ThrowPCDException(exception,message) \
{ \
if (header != (unsigned char *) NULL) \
header=(unsigned char *) RelinquishMagickMemory(header); \
if (pixel_info != (MemoryInfo *) NULL) \
pixel_info=RelinquishVirtualMemory(pixel_info); \
ThrowReaderException((exception),(message)); \
}
Image
*image;
MagickBooleanType
status;
MagickOffsetType
offset;
MemoryInfo
*pixel_info;
ssize_t
i,
y;
Quantum
*q;
unsigned char
*c1,
*c2,
*yy;
size_t
height,
number_images,
number_pixels,
rotate,
scene,
width;
ssize_t
count,
x;
unsigned char
*chroma1,
*chroma2,
*header,
*luma;
unsigned int
overview;
/*
Open image file.
*/
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
if (image_info->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
image_info->filename);
assert(exception != (ExceptionInfo *) NULL);
assert(exception->signature == MagickCoreSignature);
image=AcquireImage(image_info,exception);
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
if (status == MagickFalse)
{
image=DestroyImageList(image);
return((Image *) NULL);
}
/*
Determine if this a PCD file.
*/
header=(unsigned char *) AcquireQuantumMemory(0x800,3UL*sizeof(*header));
if (header == (unsigned char *) NULL)
ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
pixel_info=(MemoryInfo *) NULL;
count=ReadBlob(image,3*0x800,header);
if (count != (3*0x800))
ThrowPCDException(CorruptImageError,"ImproperImageHeader");
overview=LocaleNCompare((char *) header,"PCD_OPA",7) == 0;
if ((LocaleNCompare((char *) header+0x800,"PCD",3) != 0) && (overview == 0))
ThrowPCDException(CorruptImageError,"ImproperImageHeader");
rotate=header[0x0e02] & 0x03;
number_images=((header[10] << 8) | header[11]) & 0xffff;
header=(unsigned char *) RelinquishMagickMemory(header);
if ((overview != 0) &&
(AcquireMagickResource(ListLengthResource,number_images) == MagickFalse))
ThrowPCDException(ResourceLimitError,"ListLengthExceedsLimit");
/*
Determine resolution by scene specification.
*/
if ((image->columns == 0) || (image->rows == 0))
scene=3;
else
{
width=192;
height=128;
for (scene=1; scene < 6; scene++)
{
if ((width >= image->columns) && (height >= image->rows))
break;
width<<=1;
height<<=1;
}
}
if (image_info->number_scenes != 0)
scene=(size_t) MagickMin(image_info->scene,6);
if (overview != 0)
scene=1;
/*
Initialize image structure.
*/
width=192;
height=128;
for (i=1; i < (ssize_t) MagickMin(scene,3); i++)
{
width<<=1;
height<<=1;
}
image->columns=width;
image->rows=height;
image->depth=8;
for ( ; i < (ssize_t) scene; i++)
{
image->columns<<=1;
image->rows<<=1;
}
status=SetImageExtent(image,image->columns,image->rows,exception);
if (status == MagickFalse)
return(DestroyImageList(image));
status=ResetImagePixels(image,exception);
if (status == MagickFalse)
return(DestroyImageList(image));
/*
Allocate luma and chroma memory.
*/
pixel_info=AcquireVirtualMemory(image->columns+1UL,30*image->rows*
sizeof(*luma));
if (pixel_info == (MemoryInfo *) NULL)
ThrowPCDException(ResourceLimitError,"MemoryAllocationFailed");
number_pixels=(image->columns+1UL)*10*image->rows*sizeof(*luma);
luma=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
chroma1=(unsigned char *) GetVirtualMemoryBlob(pixel_info)+number_pixels;
chroma2=(unsigned char *) GetVirtualMemoryBlob(pixel_info)+2*number_pixels;
(void) memset(luma,0,3*number_pixels);
/*
Advance to image data.
*/
offset=93;
if (overview != 0)
offset=2;
else
if (scene == 2)
offset=20;
else
if (scene <= 1)
offset=1;
for (i=0; i < (ssize_t) (offset*0x800); i++)
if (ReadBlobByte(image) == EOF)
ThrowPCDException(CorruptImageError,"UnexpectedEndOfFile");
if (overview != 0)
{
MagickProgressMonitor
progress_monitor;
ssize_t
j;
/*
Read thumbnails from overview image.
*/
for (j=1; j <= (ssize_t) number_images; j++)
{
progress_monitor=SetImageProgressMonitor(image,
(MagickProgressMonitor) NULL,image->client_data);
(void) FormatLocaleString(image->filename,MagickPathExtent,
"images/img%04ld.pcd",(long) j);
(void) FormatLocaleString(image->magick_filename,MagickPathExtent,
"images/img%04ld.pcd",(long) j);
image->scene=(size_t) j;
image->columns=width;
image->rows=height;
image->depth=8;
yy=luma;
c1=chroma1;
c2=chroma2;
for (y=0; y < (ssize_t) height; y+=2)
{
count=ReadBlob(image,width,yy);
yy+=image->columns;
count=ReadBlob(image,width,yy);
yy+=image->columns;
count=ReadBlob(image,width >> 1,c1);
c1+=image->columns;
count=ReadBlob(image,width >> 1,c2);
c2+=image->columns;
if (EOFBlob(image) != MagickFalse)
ThrowPCDException(CorruptImageError,"UnexpectedEndOfFile");
}
Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
/*
Transfer luminance and chrominance channels.
*/
yy=luma;
c1=chroma1;
c2=chroma2;
for (y=0; y < (ssize_t) image->rows; y++)
{
q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) image->columns; x++)
{
SetPixelRed(image,ScaleCharToQuantum(*yy++),q);
SetPixelGreen(image,ScaleCharToQuantum(*c1++),q);
SetPixelBlue(image,ScaleCharToQuantum(*c2++),q);
q+=GetPixelChannels(image);
}
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
}
image->colorspace=YCCColorspace;
if (LocaleCompare(image_info->magick,"PCDS") == 0)
(void) SetImageColorspace(image,sRGBColorspace,exception);
if (EOFBlob(image) != MagickFalse)
break;
if (j < (ssize_t) number_images)
{
/*
Allocate next image structure.
*/
AcquireNextImage(image_info,image,exception);
if (GetNextImageInList(image) == (Image *) NULL)
{
status=MagickFalse;
break;
}
image=SyncNextImageInList(image);
}
(void) SetImageProgressMonitor(image,progress_monitor,
image->client_data);
if (image->previous == (Image *) NULL)
{
status=SetImageProgress(image,LoadImageTag,j-1,number_images);
if (status == MagickFalse)
break;
}
}
pixel_info=RelinquishVirtualMemory(pixel_info);
if (status == MagickFalse)
return(DestroyImageList(image));
return(OverviewImage(image_info,GetFirstImageInList(image),exception));
}
/*
Read interleaved image.
*/
yy=luma;
c1=chroma1;
c2=chroma2;
for (y=0; y < (ssize_t) height; y+=2)
{
count=ReadBlob(image,width,yy);
yy+=image->columns;
count=ReadBlob(image,width,yy);
yy+=image->columns;
count=ReadBlob(image,width >> 1,c1);
c1+=image->columns;
count=ReadBlob(image,width >> 1,c2);
c2+=image->columns;
if (EOFBlob(image) != MagickFalse)
ThrowPCDException(CorruptImageError,"UnexpectedEndOfFile");
}
if (scene >= 4)
{
/*
Recover luminance deltas for 1536x1024 image.
*/
Upsample(768,512,image->columns,luma);
Upsample(384,256,image->columns,chroma1);
Upsample(384,256,image->columns,chroma2);
image->rows=1024;
for (i=0; i < (4*0x800); i++)
(void) ReadBlobByte(image);
status=DecodeImage(image,luma,chroma1,chroma2,exception);
if ((scene >= 5) && status)
{
/*
Recover luminance deltas for 3072x2048 image.
*/
Upsample(1536,1024,image->columns,luma);
Upsample(768,512,image->columns,chroma1);
Upsample(768,512,image->columns,chroma2);
image->rows=2048;
offset=TellBlob(image)/0x800+12;
offset=SeekBlob(image,offset*0x800,SEEK_SET);
status=DecodeImage(image,luma,chroma1,chroma2,exception);
if ((scene >= 6) && (status != MagickFalse))
{
/*
Recover luminance deltas for 6144x4096 image (vaporware).
*/
Upsample(3072,2048,image->columns,luma);
Upsample(1536,1024,image->columns,chroma1);
Upsample(1536,1024,image->columns,chroma2);
image->rows=4096;
}
}
}
Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma1);
Upsample(image->columns >> 1,image->rows >> 1,image->columns,chroma2);
/*
Transfer luminance and chrominance channels.
*/
yy=luma;
c1=chroma1;
c2=chroma2;
for (y=0; y < (ssize_t) image->rows; y++)
{
q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) image->columns; x++)
{
SetPixelRed(image,ScaleCharToQuantum(*yy++),q);
SetPixelGreen(image,ScaleCharToQuantum(*c1++),q);
SetPixelBlue(image,ScaleCharToQuantum(*c2++),q);
q+=GetPixelChannels(image);
}
if (SyncAuthenticPixels(image,exception) == MagickFalse)
break;
if (image->previous == (Image *) NULL)
{
status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
image->rows);
if (status == MagickFalse)
break;
}
}
pixel_info=RelinquishVirtualMemory(pixel_info);
if (EOFBlob(image) != MagickFalse)
ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
image->filename);
(void) CloseBlob(image);
if (image_info->ping == MagickFalse)
if ((rotate == 1) || (rotate == 3))
{
double
degrees;
Image
*rotate_image;
/*
Rotate image.
*/
degrees=rotate == 1 ? -90.0 : 90.0;
rotate_image=RotateImage(image,degrees,exception);
if (rotate_image != (Image *) NULL)
{
image=DestroyImage(image);
image=rotate_image;
}
}
/*
Set CCIR 709 primaries with a D65 white point.
*/
image->chromaticity.red_primary.x=0.6400f;
image->chromaticity.red_primary.y=0.3300f;
image->chromaticity.green_primary.x=0.3000f;
image->chromaticity.green_primary.y=0.6000f;
image->chromaticity.blue_primary.x=0.1500f;
image->chromaticity.blue_primary.y=0.0600f;
image->chromaticity.white_point.x=0.3127f;
image->chromaticity.white_point.y=0.3290f;
image->gamma=1.000f/2.200f;
image->colorspace=YCCColorspace;
if (LocaleCompare(image_info->magick,"PCDS") == 0)
(void) SetImageColorspace(image,sRGBColorspace,exception);
if (image_info->scene != 0)
for (i=0; i < (ssize_t) image_info->scene; i++)
AppendImageToList(&image,CloneImage(image,0,0,MagickTrue,exception));
return(GetFirstImageInList(image));
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R e g i s t e r P C D I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% RegisterPCDImage() adds attributes for the PCD 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 RegisterPCDImage method is:
%
% size_t RegisterPCDImage(void)
%
*/
ModuleExport size_t RegisterPCDImage(void)
{
MagickInfo
*entry;
entry=AcquireMagickInfo("PCD","PCD","Photo CD");
entry->decoder=(DecodeImageHandler *) ReadPCDImage;
entry->encoder=(EncodeImageHandler *) WritePCDImage;
entry->magick=(IsImageFormatHandler *) IsPCD;
entry->flags^=CoderAdjoinFlag;
entry->flags|=CoderDecoderSeekableStreamFlag;
(void) RegisterMagickInfo(entry);
entry=AcquireMagickInfo("PCD","PCDS","Photo CD");
entry->decoder=(DecodeImageHandler *) ReadPCDImage;
entry->encoder=(EncodeImageHandler *) WritePCDImage;
entry->flags^=CoderAdjoinFlag;
entry->flags|=CoderDecoderSeekableStreamFlag;
(void) RegisterMagickInfo(entry);
return(MagickImageCoderSignature);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% U n r e g i s t e r P C D I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% UnregisterPCDImage() removes format registrations made by the
% PCD module from the list of supported formats.
%
% The format of the UnregisterPCDImage method is:
%
% UnregisterPCDImage(void)
%
*/
ModuleExport void UnregisterPCDImage(void)
{
(void) UnregisterMagickInfo("PCD");
(void) UnregisterMagickInfo("PCDS");
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% W r i t e P C D I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% WritePCDImage() writes an image in the Photo CD encoded image format.
%
% The format of the WritePCDImage method is:
%
% MagickBooleanType WritePCDImage(const ImageInfo *image_info,
% Image *image,ExceptionInfo *exception)
%
% A description of each parameter follows.
%
% o image_info: the image info.
%
% o image: The image.
%
% o exception: return any errors or warnings in this structure.
%
*/
static MagickBooleanType WritePCDTile(Image *image,const char *page_geometry,
const size_t tile_columns,const size_t tile_rows,ExceptionInfo *exception)
{
GeometryInfo
geometry_info;
Image
*downsample_image,
*tile_image;
MagickBooleanType
status;
MagickStatusType
flags;
RectangleInfo
geometry;
const Quantum
*p,
*q;
ssize_t
i,
x;
ssize_t
y;
/*
Scale image to tile size.
*/
SetGeometry(image,&geometry);
(void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
&geometry.width,&geometry.height);
if ((geometry.width % 2) != 0)
geometry.width--;
if ((geometry.height % 2) != 0)
geometry.height--;
tile_image=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
exception);
if (tile_image == (Image *) NULL)
return(MagickFalse);
flags=ParseGeometry(page_geometry,&geometry_info);
geometry.width=(size_t) geometry_info.rho;
geometry.height=(size_t) geometry_info.sigma;
if ((flags & SigmaValue) == 0)
geometry.height=geometry.width;
if ((tile_image->columns != geometry.width) ||
(tile_image->rows != geometry.height))
{
Image
*bordered_image;
RectangleInfo
border_info;
/*
Put a border around the image.
*/
border_info.width=(geometry.width-tile_image->columns+1) >> 1;
border_info.height=(geometry.height-tile_image->rows+1) >> 1;
bordered_image=BorderImage(tile_image,&border_info,image->compose,
exception);
if (bordered_image == (Image *) NULL)
return(MagickFalse);
tile_image=DestroyImage(tile_image);
tile_image=bordered_image;
}
if ((tile_image->columns != tile_columns) || (tile_image->rows != tile_rows))
{
Image
*resize_image;
resize_image=ResizeImage(tile_image,tile_columns,tile_rows,
tile_image->filter,exception);
if (resize_image != (Image *) NULL)
{
tile_image=DestroyImage(tile_image);
tile_image=resize_image;
}
}
(void) TransformImageColorspace(tile_image,YCCColorspace,exception);
downsample_image=ResizeImage(tile_image,tile_image->columns/2,
tile_image->rows/2,TriangleFilter,exception);
if (downsample_image == (Image *) NULL)
return(MagickFalse);
/*
Write tile to PCD file.
*/
for (y=0; y < (ssize_t) tile_image->rows; y+=2)
{
p=GetVirtualPixels(tile_image,0,y,tile_image->columns,2,exception);
if (p == (const Quantum *) NULL)
break;
for (x=0; x < (ssize_t) (tile_image->columns << 1); x++)
{
(void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelRed(tile_image,p)));
p+=GetPixelChannels(tile_image);
}
q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,1,
exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) downsample_image->columns; x++)
{
(void) WriteBlobByte(image,ScaleQuantumToChar(
GetPixelGreen(tile_image,q)));
q+=GetPixelChannels(tile_image);
}
q=GetVirtualPixels(downsample_image,0,y >> 1,downsample_image->columns,1,
exception);
if (q == (Quantum *) NULL)
break;
for (x=0; x < (ssize_t) downsample_image->columns; x++)
{
(void) WriteBlobByte(image,ScaleQuantumToChar(
GetPixelBlue(tile_image,q)));
q+=GetPixelChannels(tile_image);
}
status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
tile_image->rows);
if (status == MagickFalse)
break;
}
for (i=0; i < 0x800; i++)
(void) WriteBlobByte(image,'\0');
downsample_image=DestroyImage(downsample_image);
tile_image=DestroyImage(tile_image);
return(MagickTrue);
}
static MagickBooleanType WritePCDImage(const ImageInfo *image_info,Image *image,
ExceptionInfo *exception)
{
Image
*pcd_image;
MagickBooleanType
status;
ssize_t
i;
assert(image_info != (const ImageInfo *) NULL);
assert(image_info->signature == MagickCoreSignature);
assert(image != (Image *) NULL);
assert(image->signature == MagickCoreSignature);
if (image->debug != MagickFalse)
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
pcd_image=image;
if (image->columns < image->rows)
{
Image
*rotate_image;
/*
Rotate portrait to landscape.
*/
rotate_image=RotateImage(image,90.0,exception);
if (rotate_image == (Image *) NULL)
return(MagickFalse);
pcd_image=rotate_image;
DestroyBlob(rotate_image);
pcd_image->blob=ReferenceBlob(image->blob);
}
/*
Open output image file.
*/
status=OpenBlob(image_info,pcd_image,WriteBinaryBlobMode,exception);
if (status == MagickFalse)
{
if (pcd_image != image)
pcd_image=DestroyImage(pcd_image);
return(status);
}
if (IssRGBCompatibleColorspace(pcd_image->colorspace) == MagickFalse)
(void) TransformImageColorspace(pcd_image,sRGBColorspace,exception);
/*
Write PCD image header.
*/
for (i=0; i < 32; i++)
(void) WriteBlobByte(pcd_image,0xff);
for (i=0; i < 4; i++)
(void) WriteBlobByte(pcd_image,0x0e);
for (i=0; i < 8; i++)
(void) WriteBlobByte(pcd_image,'\0');
for (i=0; i < 4; i++)
(void) WriteBlobByte(pcd_image,0x01);
for (i=0; i < 4; i++)
(void) WriteBlobByte(pcd_image,0x05);
for (i=0; i < 8; i++)
(void) WriteBlobByte(pcd_image,'\0');
for (i=0; i < 4; i++)
(void) WriteBlobByte(pcd_image,0x0A);
for (i=0; i < 36; i++)
(void) WriteBlobByte(pcd_image,'\0');
for (i=0; i < 4; i++)
(void) WriteBlobByte(pcd_image,0x01);
for (i=0; i < 1944; i++)
(void) WriteBlobByte(pcd_image,'\0');
(void) WriteBlob(pcd_image,7,(const unsigned char *) "PCD_IPI");
(void) WriteBlobByte(pcd_image,0x06);
for (i=0; i < 1530; i++)
(void) WriteBlobByte(pcd_image,'\0');
if (image->columns < image->rows)
(void) WriteBlobByte(pcd_image,'\1');
else
(void) WriteBlobByte(pcd_image,'\0');
for (i=0; i < (3*0x800-1539); i++)
(void) WriteBlobByte(pcd_image,'\0');
/*
Write PCD tiles.
*/
status=WritePCDTile(pcd_image,"768x512>",192,128,exception);
status=WritePCDTile(pcd_image,"768x512>",384,256,exception);
status=WritePCDTile(pcd_image,"768x512>",768,512,exception);
(void) CloseBlob(pcd_image);
if (pcd_image != image)
pcd_image=DestroyImage(pcd_image);
return(status);
}