diff options
Diffstat (limited to 'coders/wpg.c')
-rw-r--r-- | coders/wpg.c | 445 |
1 files changed, 404 insertions, 41 deletions
diff --git a/coders/wpg.c b/coders/wpg.c index edfedc3..25fa13b 100644 --- a/coders/wpg.c +++ b/coders/wpg.c @@ -1,5 +1,5 @@ /* -% Copyright (C) 2003-2021 GraphicsMagick Group +% Copyright (C) 2003-2023 GraphicsMagick Group % Copyright (C) 2002 ImageMagick Studio % % This program is covered by multiple licenses, which are described in @@ -21,7 +21,7 @@ % % % Software Design % % Jaroslav Fojtik % -% June 2000 - 2021 % +% June 2000 - 2023 % % Rework for GraphicsMagick % % Bob Friesenhahn % % Feb-May 2003 % @@ -46,6 +46,7 @@ #include "magick/shear.h" #include "magick/tempfile.h" #include "magick/transform.h" +#include "magick/quantize.h" #include "magick/utility.h" @@ -189,9 +190,19 @@ static const RGB_Record WPG1_Palette[256]={ }; -static int ApproveFormatForWPG(const char *Format) +static int ApproveFormatForWPG(const char *Format, ExceptionInfo *exception) { + const MagickInfo *magick_info; + if(!strcmp(Format,"PFB")) return 0; + + /*if(!strcmp(Format,"8BIMTEXT")) return 0; This test is no longer needed, META module includes this case. */ + magick_info = GetMagickInfo(Format,exception); + if(magick_info != (const MagickInfo *)NULL) + { + if(strcmp(magick_info->module, "META") == 0) return 0; + } + return 1; } @@ -235,28 +246,6 @@ static unsigned int IsWPG(const unsigned char *magick,const size_t length) return(False); } -static MagickPassFail ReallocColormap(Image *image,unsigned int colors) -{ - PixelPacket *colormap; - - /* FIXME: This implementation would be better using a true realloc */ - colormap=MagickAllocateClearedArray(PixelPacket *,colors,sizeof(PixelPacket)); - if (colormap != (PixelPacket *) NULL) - { - if (image->colormap != (PixelPacket *) NULL) - { - (void) memcpy(colormap,image->colormap, - (size_t) Min(image->colors,colors)*sizeof(PixelPacket)); - MagickFreeMemory(image->colormap); - } - image->colormap = colormap; - image->colors = colors; - return MagickPass; - } - - return MagickFail; -} - static int Rd_WP_DWORD(Image *image, unsigned long *d) { unsigned char b; @@ -886,7 +875,7 @@ static Image *ExtractPostscript(Image *image,const ImageInfo *image_info, /* Verify if this is an allowed subordinate image format */ - if(!ApproveFormatForWPG(format)) + if(!ApproveFormatForWPG(format,exception)) { (void) LogMagickEvent(CoderEvent, GetMagickModule(), "Format \"%s\" cannot be embedded inside WPG.", format); @@ -975,7 +964,7 @@ static Image *ExtractPostscript(Image *image,const ImageInfo *image_info, clone_info->blob=(void *) NULL; /* clone_info->length=0; */ (void) strlcpy(clone_info->magick, format, sizeof(clone_info->magick)); - (void) strcpy(clone_info->filename, ""); + (void) strlcpy(clone_info->filename, "", sizeof(clone_info->filename)); (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Reading embedded \"%s\" content from blob...", clone_info->magick); image2 = BlobToImage(clone_info, ps_data, PS_Size, &image->exception ); @@ -984,8 +973,18 @@ static Image *ExtractPostscript(Image *image,const ImageInfo *image_info, { goto FINISH_UNL; } - if(exception->severity>=ErrorException) /* When exception is raised, destroy image2 read. */ + if(exception->severity >= ErrorException) /* When exception is raised, destroy image2 read. */ { + if(image->logging) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Exception raised during embedded image reading."); + CloseBlob(image2); + DestroyImageList(image2); + goto FINISH_UNL; + } + if(!GetPixelCachePresent(image2)) + { + if(image->logging) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Pixel cache is missing for embedded image."); CloseBlob(image2); DestroyImageList(image2); goto FINISH_UNL; @@ -1037,6 +1036,62 @@ static Image *ExtractPostscript(Image *image,const ImageInfo *image_info, return(image); } +#define LogHeaderWPG(_WPG_HEADER) \ + (void)LogMagickEvent(CoderEvent,GetMagickModule(), \ + "WPG Header Id=%Xh:\n" \ + " DataOffset=%u\n" \ + " ProductType=%u\n" \ + " FileType=%u\n" \ + " Version=%u.%u\n" \ + " EncryptKey=%u\n" \ + " Reserved=%u", \ + (unsigned int)_WPG_HEADER.FileId, \ + (unsigned int)_WPG_HEADER.DataOffset, \ + (unsigned int)_WPG_HEADER.ProductType, \ + (unsigned int)_WPG_HEADER.FileType, \ + (unsigned int)_WPG_HEADER.MajorVersion, \ + (unsigned int)_WPG_HEADER.MinorVersion, \ + (unsigned int)_WPG_HEADER.EncryptKey, \ + (unsigned int)_WPG_HEADER.Reserved) + +#define LogWPGBitmapType1(_WPG_BITMAP_TYPE1) \ + (void)LogMagickEvent(CoderEvent,GetMagickModule(), \ + "WPG Bitmap1 Header:\n" \ + " Width=%u\n" \ + " Heigth=%u\n" \ + " Depth=%u\n" \ + " HorzRes=%u\n" \ + " VertRes=%u", \ + (unsigned int)_WPG_BITMAP_TYPE1.Width, \ + (unsigned int)_WPG_BITMAP_TYPE1.Heigth, \ + (unsigned int)_WPG_BITMAP_TYPE1.Depth, \ + (unsigned int)_WPG_BITMAP_TYPE1.HorzRes, \ + (unsigned int)_WPG_BITMAP_TYPE1.VertRes) + +#define LogWPGBitmapType2(_WPG_BITMAP_TYPE2) \ + (void)LogMagickEvent(CoderEvent,GetMagickModule(), \ + "WPG Bitmap2 Header:\n" \ + " RotAngle=%u\n" \ + " LowLeftX=%u\n" \ + " LowLeftY=%u\n" \ + " UpRightX=%u\n" \ + " UpRightY=%u\n" \ + " Width=%u\n" \ + " Heigth=%u\n" \ + " Depth=%u\n" \ + " HorzRes=%u\n" \ + " VertRes=%u", \ + (unsigned int)_WPG_BITMAP_TYPE2.RotAngle, \ + (unsigned int)_WPG_BITMAP_TYPE2.LowLeftX, \ + (unsigned int)_WPG_BITMAP_TYPE2.LowLeftY, \ + (unsigned int)_WPG_BITMAP_TYPE2.UpRightX, \ + (unsigned int)_WPG_BITMAP_TYPE2.UpRightY, \ + (unsigned int)_WPG_BITMAP_TYPE2.Width, \ + (unsigned int)_WPG_BITMAP_TYPE2.Heigth, \ + (unsigned int)_WPG_BITMAP_TYPE2.Depth, \ + (unsigned int)_WPG_BITMAP_TYPE2.HorzRes, \ + (unsigned int)_WPG_BITMAP_TYPE2.VertRes) + /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1067,7 +1122,6 @@ static Image *ExtractPostscript(Image *image,const ImageInfo *image_info, % % o exception: return any errors or warnings in this structure. % -% */ static Image *ReadWPGImage(const ImageInfo *image_info, ExceptionInfo *exception) @@ -1076,8 +1130,8 @@ static Image *ReadWPGImage(const ImageInfo *image_info, { unsigned long FileId; ExtendedSignedIntegralType DataOffset; /* magick_uint32_t */ - unsigned int ProductType; - unsigned int FileType; + unsigned char ProductType; + unsigned char FileType; unsigned char MajorVersion; unsigned char MinorVersion; unsigned int EncryptKey; @@ -1217,14 +1271,15 @@ static Image *ReadWPGImage(const ImageInfo *image_info, */ Header.FileId=ReadBlobLSBLong(image); Header.DataOffset=(ExtendedSignedIntegralType) ReadBlobLSBLong(image); - Header.ProductType=ReadBlobLSBShort(image); - Header.FileType=ReadBlobLSBShort(image); + Header.ProductType=ReadBlobByte(image); + Header.FileType=ReadBlobByte(image); Header.MajorVersion=ReadBlobByte(image); Header.MinorVersion=ReadBlobByte(image); Header.EncryptKey=ReadBlobLSBShort(image); Header.Reserved=ReadBlobLSBShort(image); + if(logging) LogHeaderWPG(Header); - if (Header.FileId!=0x435057FF || (Header.ProductType>>8)!=0x16) + if (Header.FileId!=0x435057FF || Header.FileType!=0x16) ThrowReaderException(CorruptImageError,ImproperImageHeader,image); if (Header.EncryptKey!=0) ThrowReaderException(CoderError,EncryptedWPGImageFileNotSupported,image); @@ -1251,7 +1306,7 @@ static Image *ReadWPGImage(const ImageInfo *image_info, ThrowReaderException(CorruptImageError,AnErrorHasOccurredReadingFromFile,image); } - switch(Header.FileType) + switch(Header.MajorVersion) { case 1: /* WPG level 1 */ BitmapHeader2.RotAngle = 0; @@ -1293,6 +1348,7 @@ static Image *ReadWPGImage(const ImageInfo *image_info, BitmapHeader1.Depth=ReadBlobLSBShort(image); BitmapHeader1.HorzRes=ReadBlobLSBShort(image); BitmapHeader1.VertRes=ReadBlobLSBShort(image); + if(logging) LogWPGBitmapType1(BitmapHeader1); if(BitmapHeader1.HorzRes && BitmapHeader1.VertRes) { @@ -1303,7 +1359,16 @@ static Image *ReadWPGImage(const ImageInfo *image_info, image->columns=BitmapHeader1.Width; image->rows=BitmapHeader1.Heigth; bpp=BitmapHeader1.Depth; - + // Whole palette is useless for bilevel image. + if(bpp==1) + { + image->storage_class = PseudoClass; + image->colors = 2; + if (!AllocateImageColormap(image,2)) goto NoMemory; + image->colormap[0].red = image->colormap[0].green = image->colormap[0].blue = 0; + image->colormap[1].red = image->colormap[1].green = image->colormap[1].blue = MaxRGB; + image->colormap[0].opacity = image->colormap[1].opacity = OpaqueOpacity; + } goto UnpackRaster; case 0x0E: /*Color palette */ @@ -1344,6 +1409,7 @@ static Image *ReadWPGImage(const ImageInfo *image_info, BitmapHeader2.Depth=ReadBlobLSBShort(image); BitmapHeader2.HorzRes=ReadBlobLSBShort(image); BitmapHeader2.VertRes=ReadBlobLSBShort(image); + if(logging) LogWPGBitmapType2(BitmapHeader2); image->units=PixelsPerCentimeterResolution; image->page.width=(unsigned int) @@ -1365,7 +1431,7 @@ UnpackRaster: if(bpp>24) {ThrowReaderException(CoderError,ColorTypeNotSupported,image)} - if ((image->storage_class != PseudoClass) && (bpp != 24)) + if ((image->storage_class != PseudoClass) && (bpp != 24) && bpp!=1) { image->colors=1 << bpp; if (!AllocateImageColormap(image,image->colors)) @@ -1387,8 +1453,8 @@ UnpackRaster: else { if (bpp < 24) - if ( (image->colors < (1UL<<bpp)) && (bpp != 24) ) - if (!ReallocColormap(image,1U<<bpp)) + if ( (image->colors != (1UL<<bpp)) && (bpp != 24) ) + if (!ReallocateImageColormap(image,1U<<bpp)) goto NoMemory; } @@ -1589,8 +1655,8 @@ UnpackRaster: else { if(bpp < 24) - if( image->colors<(1UL<<bpp) && bpp!=24 ) - if (!ReallocColormap(image,1U<<bpp)) + if(image->colors!=(1UL<<bpp) && bpp!=24) + if (!ReallocateImageColormap(image,1U<<bpp)) goto NoMemory; } @@ -1742,6 +1808,301 @@ UnpackRaster: ThrowReaderException(CorruptImageError,ImageFileDoesNotContainAnyImageData,image); return(image); } + + +/* RLE helper structure. */ +typedef struct +{ + unsigned char count; + unsigned char pos; + unsigned char buf[254]; +} WPG_RLE_packer; + +//FILE *DebugRLE = NULL; + +static void WPG_RLE_Flush(WPG_RLE_packer *WPG_RLE, Image *image, unsigned char n) +{ + if(n>WPG_RLE->pos) n=WPG_RLE->pos; + if(n>0x7F) n=0x7F; + if(n>0) + { + //if(DebugRLE) fprintf(DebugRLE," size=%X",n); + WriteBlobByte(image,n); + WriteBlob(image, n, WPG_RLE->buf); + WPG_RLE->pos -= n; + if(WPG_RLE->pos>0) + memcpy(WPG_RLE->buf, WPG_RLE->buf+n, n); + else + WPG_RLE->count = 0; + } +} + + +static void WPG_RLE_AddCharacter(WPG_RLE_packer *WPG_RLE, unsigned char b, Image *image) +{ + WPG_RLE->buf[WPG_RLE->pos++] = b; + +/* if(DebugRLE) + { + fprintf(DebugRLE,"\n%u",b); + if(WPG_RLE->pos>=0x7E) + fprintf(DebugRLE," *%u %X", WPG_RLE->pos, WPG_RLE->pos); + } */ + + if(WPG_RLE->pos>1) + { + if(WPG_RLE->count==0x7E || WPG_RLE->buf[WPG_RLE->pos-2]!=b) + { + if(WPG_RLE->count>=1) + { + WPG_RLE->count++; // True number of repeated BYTEs. + WPG_RLE_Flush(WPG_RLE, image, WPG_RLE->pos-1-WPG_RLE->count); + WriteBlobByte(image, WPG_RLE->count|0x80); + WriteBlobByte(image, WPG_RLE->buf[0]); + //if(DebugRLE) fprintf(DebugRLE," count=%X, val=%X",WPG_RLE->count,WPG_RLE->buf[0]); + WPG_RLE->pos = 1; + WPG_RLE->buf[0] = b; + } + WPG_RLE->count = 0; + } + else + WPG_RLE->count++; + } + + if(WPG_RLE->pos-WPG_RLE->count>0x7E) // We have uncompressible block with size 0x7F. + { + WPG_RLE_Flush(WPG_RLE, image, 0x7F); + return; + } + if(WPG_RLE->pos>0x7E && WPG_RLE->count>=1) + { + WPG_RLE_Flush(WPG_RLE, image, WPG_RLE->pos-1-WPG_RLE->count); + return; + } +} + + +static void WPG_RLE_FinalFlush(WPG_RLE_packer *WPG_RLE, Image *image) +{ + if(WPG_RLE->count>1) + { + WPG_RLE_AddCharacter(WPG_RLE, WPG_RLE->buf[WPG_RLE->pos-1]^0xFF, image); // Add a fake BYTE. + WPG_RLE->pos = 0; // Take a last fake BYTE away. + } + else + { + WPG_RLE_Flush(WPG_RLE,image,0x7F); + WPG_RLE_Flush(WPG_RLE,image,0x7F); + WPG_RLE->count = 0; + } +} + + +static void WPG_RLE_AddBlock(WPG_RLE_packer *WPG_RLE, Image *image, const unsigned char *blk, magick_uint16_t size) +{ + while(size-- > 0) + { + WPG_RLE_AddCharacter(WPG_RLE, *blk, image); + blk++; + } +} + + +static void WPG_RLE_Init(WPG_RLE_packer *WPG_RLE) +{ + WPG_RLE->pos = WPG_RLE->count = 0; +} + + +/* +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% % +% % +% W r i t e W P G I m a g e % +% % +% % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Function WriteWPGImage writes an WPG image to a file. +% +% The format of the WriteWPGImage method is: +% +% unsigned int WriteWPGImage(const ImageInfo *image_info,Image *image) +% +% A description of each parameter follows. +% +% o status: Function WriteWPGImage 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 MagickPassFail WriteWPGImage(const ImageInfo *image_info, Image *image) +{ + long y; + unsigned int status; + int logging; + unsigned char StoredPlanes; + unsigned ldblk; + magick_off_t NumericOffs, CurrOffs; + QuantizeInfo quantize_info; + WPG_RLE_packer PackRLE; + unsigned char *pixels; + + /* + Open output image file. + */ + assert(image_info != (const ImageInfo *) NULL); + assert(image_info->signature == MagickSignature); + assert(image != (Image *) NULL); + assert(image->signature == MagickSignature); + logging = LogMagickEvent(CoderEvent,GetMagickModule(),"enter WPG"); + status = OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); + if(status == MagickFail) + ThrowWriterException(FileOpenError,UnableToOpenFile,image); + WPG_RLE_Init(&PackRLE); + + (void)TransformColorspace(image,RGBColorspace); + + if ((image->storage_class == DirectClass) || + ((image->storage_class == PseudoClass) && (image->colors<=0 || image->colors>256))) + { + GetQuantizeInfo(&quantize_info); + quantize_info.dither = image_info->dither; + quantize_info.number_colors = 256; + status = QuantizeImage(&quantize_info,image); + if(status==MagickFail || image->colors==0) goto ImageFailure; + } + if(image->colors <= 2) + { + StoredPlanes=1; + ldblk = (image->columns+7)/8; + } else if(image->colors <= 16) + { + StoredPlanes=4; + ldblk = (image->columns+1)/2; + } else + { + StoredPlanes = 8; + ldblk = image->columns; + } + + pixels = MagickAllocateResourceLimitedMemory(unsigned char *,(size_t)(ldblk)); + if(pixels == (unsigned char *) NULL) + ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image); + + /* Write WPG hader. */ + WriteBlobLSBLong(image,0x435057FF); //DWORD FileId + WriteBlobLSBLong(image,16); //DWORD DataOffset; + WriteBlobByte(image,1); //BYTE Product Type + WriteBlobByte(image,0x16); //BYTE FileType; + WriteBlobByte(image,1); //BYTE MajorVersion; + WriteBlobByte(image,0); //BYTE MinorVersion; + WriteBlobLSBShort(image,0); //WORD EncryptKey; + WriteBlobLSBShort(image,0); //WORD Reserved; + + /* Start WPG l1 */ + WriteBlobByte(image,0xF); + WriteBlobByte(image,0x6); + WriteBlobByte(image,1); //BYTE Version number + WriteBlobByte(image,0); //BYTE Flags (bit 0 PostScript, maybe bitmap, bit 1 PostScript, no bitmap + WriteBlobLSBShort(image,image->columns); //WORD Width of image (arbitrary units) + WriteBlobLSBShort(image,image->rows); //WORD Height of image (arbitrary units) + + /* Palette */ + if(StoredPlanes>1) + { + magick_uint16_t i; + WriteBlobByte(image,0xE); + i = 4+3*(1<<StoredPlanes); + if(i<0xFF) + WriteBlobByte(image,i); + else + { + WriteBlobByte(image,0xFF); + WriteBlobLSBShort(image,i); + } + WriteBlobLSBShort(image,0); // Start index + WriteBlobLSBShort(image,1<<StoredPlanes); // Num entries + + for(i=0; i<(1<<StoredPlanes); i++) + { + if(i>=image->colors || image->colormap==NULL) + { + WriteBlobByte(image,i); + WriteBlobByte(image,i); + WriteBlobByte(image,i); + } + else + { + WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red)); + WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].green)); + WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue)); + } + } + } + + /* Bitmap 1 header */ + WriteBlobByte(image,0xB); + WriteBlobByte(image,0xFF); + NumericOffs = TellBlob(image); + WriteBlobLSBShort(image,0x8000); + WriteBlobLSBShort(image,0); + + WriteBlobLSBShort(image,image->columns); //WORD Width + WriteBlobLSBShort(image,image->rows); //WORD Height + WriteBlobLSBShort(image,StoredPlanes); //WORD Depth; + WriteBlobLSBShort(image,75); //WORD HorzRes; + WriteBlobLSBShort(image,75); //WORD VertRes; + + /* + Store image data. + */ + for(y=0; y<(long)image->rows; y++) + { + //if(y==1310 && DebugRLE==NULL) DebugRLE=fopen("o:\\temp\\14\\debug.txt","wb"); + //if(y>1310 && DebugRLE) {fclose(DebugRLE);DebugRLE=NULL;} + + if(AcquireImagePixels(image,0,y,image->columns,1,&image->exception) == (const PixelPacket *)NULL) + { + status = MagickFail; + break; + } + if(ExportImagePixelArea(image, (StoredPlanes==1)?GrayQuantum:IndexQuantum, StoredPlanes,pixels,0,0) != MagickPass) + { + status = MagickFail; + break; + } + WPG_RLE_AddBlock(&PackRLE,image,pixels,ldblk); + WPG_RLE_FinalFlush(&PackRLE,image); + } + + CurrOffs = TellBlob(image); + SeekBlob(image,NumericOffs,SEEK_SET); + NumericOffs = CurrOffs - NumericOffs - 4; + WriteBlobLSBShort(image, 0x8000|(NumericOffs>>16)); + WriteBlobLSBShort(image, NumericOffs&0xFFFF); + SeekBlob(image,CurrOffs,SEEK_SET); + + /* End of WPG data */ + WriteBlobByte(image,0x10); + WriteBlobByte(image,0); + + MagickFreeResourceLimitedMemory(pixels); +ImageFailure: + CloseBlob(image); + + + if (logging) + (void)LogMagickEvent(CoderEvent,GetMagickModule(),"return WPG"); + + return(status); +} + /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1773,10 +2134,12 @@ ModuleExport void RegisterWPGImage(void) entry=SetMagickInfo("WPG"); entry->decoder=(DecoderHandler) ReadWPGImage; + entry->encoder = (EncoderHandler)WriteWPGImage; entry->magick=(MagickHandler) IsWPG; entry->description="Word Perfect Graphics"; entry->module="WPG"; entry->seekable_stream=True; + entry->adjoin=MagickFalse; entry->coder_class=UnstableCoderClass; (void) RegisterMagickInfo(entry); } |