|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% 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);
|
|
|
}
|