/******************************************************************************
 * program:     rasimg library                                                *
 * function:    Library for saving/loading pictures                           *
 * modul:       ras_bmp.cc                                                    *
 * licency:     GPL or LGPL                                                   *
 ******************************************************************************/
#include <stdio.h>
#include <stdlib.h>

#include "typedfs.h"
#include "common.h"
#include "image.h"
#include "img_tool.h"
#include "ras_prot.h"
#include "std_str.h"

#ifndef __UNIX__
 #include <io.h>
#endif

#include "imgsupp.h"
#include "struct.h"

#define Rd_word(f,w) RdWORD_LoEnd(w,f);
#define Rd_dword(f,d) RdDWORD_LoEnd(d,f);
#define Wr_word(f,w) WrWORD_LoEnd(w,f);
#define Wr_dword(f,d) WrDWORD_LoEnd(d,f);


#ifdef __PackedStructures__
 #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(ewarm)
   #define __PACKING__
 #endif
#else
 #if defined(PACKED)
  #undef PACKED
 #endif
 #define PACKED
#endif


// https://www.daubnet.com/en/file-format-cur

//--------------------------BMP---------------------------
#if defined(SupportBMP) || defined(SupportDIB) || defined(SupportICO)

#if SupportJPG>=4 || SupportJPG==2
Raster2DAbstract *LoadFragmentJPG(FILE *F);
#endif
#if SupportPNG>=4 || SupportPNG==2
void LoadFragmentPNG(FILE *F, Image *Img, unsigned char EnableMng);
#endif

typedef enum
{
  BI_RGB = 0,	//An uncompressed format.
  BI_RLE8,	//A run-length encoded (RLE) format for bitmaps with 8 bpp. 
		//The compression format is a 2-byte format consisting of a count byte followed by a byte containing a color index.
  BI_RLE4,	//An RLE format for bitmaps with 4 bpp. The compression format is a 2-byte format consisting of a count byte followed by two word-length color indexes.
  BI_BITFIELDS, //Specifies that the bitmap is not compressed. The members bV4RedMask, bV4GreenMask, and bV4BlueMask specify the red, green, and blue components for each pixel. This is valid when used with 16- and 32-bpp bitmaps.
  BI_JPEG,	//Specifies that the image is compressed using the JPEG file interchange format. JPEG compression trades off compression against loss.
  BI_PNG,	//Specifies that the image is compressed using the PNG file interchange format.
  BI_ALPHABITFIELDS,
  BI_CMYK = 0x0B,	//The image is an uncompressed CMYK format.
  BI_CMYKRLE8 = 0x0C,	//A CMYK format that uses RLE compression for bitmaps with 8 bits per pixel. The compression uses a 2-byte format consisting of a count byte followed by a byte containing a color index.
  BI_CMYKRLE4 = 0x0D	//A CMYK format that uses RLE compression for bitmaps with 4 bits per pixel. The compression uses a 2-byte format consisting of a count byte followed by two word-length color indexes.
} BmpCompression;

typedef int32_t FXPT2DOT30;

#ifdef __PACKING__
 #pragma pack(push, 1)
#endif

typedef struct {
  FXPT2DOT30 ciexyzX;
  FXPT2DOT30 ciexyzY;
  FXPT2DOT30 ciexyzZ;
} CIEXYZ;

typedef struct
{
  CIEXYZ ciexyzRed;
  CIEXYZ ciexyzGreen;
  CIEXYZ ciexyzBlue;
} CIEXYZTRIPLE;

typedef struct
	{
	uint8_t    bfType[2]	PACKED;
	uint32_t   bfSize		PACKED;
	uint16_t    bfReserved1	PACKED;
	uint16_t    bfReserved2	PACKED;
	uint32_t   bfOffBits	PACKED;
	} BMPHeader;


///OS/2 V1  BITMAPCOREHEADER and also all Windows versions since Windows 3.0
typedef struct
	{
	uint32_t bcSize		PACKED;
	uint16_t  bcWidth	PACKED;
	uint16_t  bcHeight	PACKED;
	uint16_t  bcPlanes	PACKED;
	uint16_t  bcBitCount	PACKED;
	} BitmapCoreInfoHeader;

//Windows V3  BITMAPINFOHEADER  all Windows versions since Windows 3.0
typedef struct
	{
	uint32_t  biSize		PACKED;
	uint32_t  biWidth		PACKED;
	int32_t  biHeight		PACKED;
	uint16_t   biPlanes		PACKED;
	uint16_t   biBitCount	PACKED;
	uint32_t  biCompression	PACKED;
	uint32_t  biSizeImage	PACKED;
	int32_t biXPelsPerMeter	PACKED;
	int32_t biYPelsPerMeter	PACKED;
	uint32_t  biClrUsed	PACKED;
	uint32_t  biClrImportant	PACKED;
	} BitmapInfoHeader;

typedef struct
	{
	uint32_t	bV4Size;
	int32_t		bV4Width;
	int32_t		bV4Height;
	uint16_t	bV4Planes;
	uint16_t	bV4BitCount;
	uint32_t	bV4Compression;
	uint32_t	bV4SizeImage;
	int32_t	bV4XPelsPerMeter;
	int32_t	bV4YPelsPerMeter;
	uint32_t	bV4ClrUsed;
	uint32_t	bV4ClrImportant;   // <-- same as BitmapInfoHeader to this point.
	uint32_t	bV4RedMask;
	uint32_t	bV4GreenMask;
	uint32_t	bV4BlueMask;
	uint32_t	bV4AlphaMask;
	uint32_t	bV4CSType;
	CIEXYZTRIPLE bV4Endpoints;
	uint32_t	bV4GammaRed;
	uint32_t	bV4GammaGreen;
	uint32_t	bV4GammaBlue;
} BitmapV4Header;

typedef struct BitmapV5Header
{
	uint32_t	bV5Size;
	uint32_t	bV5Width;
	int32_t		bV5Height;
	uint16_t	bV5Planes;
	uint16_t	bV5BitCount;
	uint32_t	bV5Compression;
	uint32_t	bV5SizeImage;
	int32_t	bV5XPelsPerMeter;
	int32_t	bV5YPelsPerMeter;
	uint32_t	bV5ClrUsed;
	uint32_t	bV5ClrImportant;
	uint32_t	bV5RedMask;
	uint32_t	bV5GreenMask;
	uint32_t	bV5BlueMask;
	uint32_t	bV5AlphaMask;
	uint32_t	bV5CSType;
	CIEXYZTRIPLE bV5Endpoints;
	uint32_t	bV5GammaRed;
	uint32_t	bV5GammaGreen;
	uint32_t	bV5GammaBlue;
	uint32_t	bV5Intent;
	uint32_t	bV5ProfileData;
	uint32_t	bV5ProfileSize;
	uint32_t	bV5Reserved;
} BitmapV5Header;


typedef struct _Os22xBitmapHeader
{
	uint32_t  Size;             /* Size of this structure in bytes */
	uint32_t  Width;            /* Bitmap width in pixels */
	uint32_t  Height;           /* Bitmap height in pixel */
	uint16_t  NumPlanes;        /* Number of bit planes (color depth) */
	uint16_t  BitsPerPixel;     /* Number of bits per pixel per plane */
		/* Fields added for OS/2 2.x follow this line */
	uint32_t  Compression;      /* Bitmap compression scheme */
	uint32_t  ImageDataSize;    /* Size of bitmap data in bytes */
	uint32_t  XResolution;      /* X resolution of display device */
	uint32_t  YResolution;      /* Y resolution of display device */
	uint32_t  ColorsUsed;       /* Number of color table indices used */
	uint32_t  ColorsImportant;  /* Number of important color indices */
	uint16_t  Units;            /* Type of units used to measure resolution */
	uint16_t  Reserved;         /* Pad structure to 4-byte boundary */
	uint16_t  Recording;        /* Recording algorithm */
	uint16_t  Rendering;        /* Halftoning algorithm used */
	uint32_t  Size1;            /* Reserved for halftoning algorithm use */
	uint32_t  Size2;            /* Reserved for halftoning algorithm use */
	uint32_t  ColorEncoding;    /* Color model used in bitmap */
	uint32_t  Identifier;       /* Reserved for application use */
} OS22XBITMAPHEADER;


union BMPInfo_Coreinfo
	{
	BitmapInfoHeader     BIH;
	BitmapV4Header	     BIHv4;
	BitmapV5Header       BIHv5;
	BitmapCoreInfoHeader BCIH;
	OS22XBITMAPHEADER    OS2X;
	};

typedef struct
	{
	uint8_t rgbBlue;
	uint8_t rgbGreen;
	uint8_t rgbRed;
	uint8_t rgbReserved;
	} BMP_RGBQuad;

#ifdef __PACKING__
 #pragma pack(pop)
#endif


#if SupportBMP>=4 || SupportBMP==2 || SupportDIB>=4 || SupportDIB==2


#ifdef __PACKING__
 #pragma pack(push, 1)
#endif

typedef struct
	{
	uint8_t StartBit;
	uint8_t BitCount;
	uint32_t Multiplier;
	} TBitScaller;

#ifdef __PACKING__
 #pragma pack(pop)
#endif



inline long LoadPartBmpCoreInfHdr(FILE *f, BitmapCoreInfoHeader &SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU.bcWidth,1,sizeof(SU)-4,f));
#else
 return(loadstruct(f,"wwww",
	 &SU.bcWidth,&SU.bcHeight,&SU.bcPlanes,&SU.bcBitCount));
#endif
}

inline long LoadPartBmpInfHdr(FILE *f, BitmapInfoHeader &SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU.biWidth,1,sizeof(SU)-4,f));
#else
 return(loadstruct(f,"ddwwdddddd",
	 &SU.biWidth, &SU.biHeight, &SU.biPlanes,
	 &SU.biBitCount, &SU.biCompression, &SU.biSizeImage, &SU.biXPelsPerMeter,
	 &SU.biYPelsPerMeter, &SU.biClrUsed, &SU.biClrImportant));
#endif
}

inline long LoadBmpHeader(FILE *f, BMPHeader &SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(SU),f));
#else
 return(loadstruct(f,"a2dwwd",
	 &SU.bfType, &SU.bfSize, &SU.bfReserved1,
	 &SU.bfReserved2, &SU.bfOffBits));
#endif
}

void LoadBmpStream(Image &Img, FILE *f, uint32_t Header_bfOffBits);

Image LoadPictureBMP(const char *Name)
{
Image Img;
FILE *f;
BMPHeader Header;
Image *CurrImg = &Img;

 if((f=fopen(Name,"rb"))==NULL) return(Img);

 if(LoadBmpHeader(f,Header)!=14) goto ENDPROC;
 if((Header.bfType[0]!='B')||(Header.bfType[1]!='M')) goto ENDPROC;

 LoadBmpStream(Img,f,Header.bfOffBits);

 while(!feof(f))
 {
   long FilePos = ftell(f);
   if(LoadBmpHeader(f,Header)!=14) break;
   if((Header.bfType[0]!='B')||(Header.bfType[1]!='M')) break;

   while(CurrImg->Next != NULL) CurrImg=CurrImg->Next;	// Seek to the end of chain.
   CurrImg->Next = new Image;
   CurrImg = CurrImg->Next;
   LoadBmpStream(*CurrImg, f, FilePos+Header.bfOffBits);
 }

ENDPROC:
 fclose(f);
 return(Img);
}


/** Entry point for loading device independent bitmap. */
Image LoadPictureDIB(const char *Name)
{
Image Img;
FILE *f;

 if((f=fopen(Name,"rb"))==NULL) return(Img);

 LoadBmpStream(Img,f,0);

 fclose(f);
 return(Img);
}


void CalcScaller32(TBitScaller & Scl, uint32_t Mask)
{
  Scl.StartBit = 0;
  while(Mask!=0 && (Mask & 1)==0)
  {
    Scl.StartBit++;
    Mask >>= 1;
  }

  Scl.BitCount = 0;
  if(Mask==0)
  {
    Scl.Multiplier = 0;
    return;
  }
  else
    Scl.Multiplier = 0xFFFFFFFF / Mask;

  while((Mask & 1)==1)
  {
    Scl.BitCount++;
    Mask >>= 1;
  }
}


/** This procedure loads BMP without a header. It is usefull e.g. for reading WMF.*/
void LoadBmpStream(Image &Img, FILE *f, uint32_t Header_bfOffBits)
{
uint16_t ldblk;
uint16_t i;
uint16_t w;
uint8_t k;
char Compression = 0;
BMPInfo_Coreinfo Info;
BMP_RGBQuad PalItem;
Raster2DAbstract *Raster=NULL;
Raster2DAbstractRGB *RasterRGB = NULL;
APalette *Palette=NULL;

 if(RdDWORD_LoEnd(&Info.BIH.biSize,f)!=4) return;

 ldblk = 0;		// amount of colors in the pelette
 switch(Info.BIH.biSize)
	{
	case 12:	//OS/2 V1  BITMAPCOREHEADER and also all Windows versions since Windows 3.0
	   {
	   if(LoadPartBmpCoreInfHdr(f,Info.BCIH) != (12-4)) goto ENDPROC;
	   if(Info.BCIH.bcPlanes!=1) return;
           if(Info.BCIH.bcBitCount==24 && Info.BCIH.bcPlanes==1)
             Raster = CreateRaster2DRGB(Info.BCIH.bcWidth,Info.BCIH.bcHeight,8);
           else
	     Raster = CreateRaster2D(Info.BCIH.bcWidth,Info.BCIH.bcHeight,
				 Info.BCIH.bcPlanes*Info.BCIH.bcBitCount);
	   break;
	   }

	case 108:	//Windows V4 	BITMAPV4HEADER 	all Windows versions since Windows 95/NT4
        case 124:	//Windows V5 	BITMAPV5HEADER 	Windows 98/2000 and newer
				// fallback; sometimes  this occurs
	case 40:	//Windows V3  BITMAPINFOHEADER  all Windows versions since Windows 3.0
	case 52:	// BITMAPV2INFOHEADER
	case 56:	// BITMAPV3INFOHEADER
        case 78:
        case 64:	//OS/2 V2  BITMAPCOREHEADER2
	   {
	   if(LoadPartBmpInfHdr(f,Info.BIH) != (40-4)) goto ENDPROC;
	   if(Info.BIH.biPlanes!=1) return;
	   Compression = Info.BIH.biCompression;
#if SupportJPG>=4 || SupportJPG==2
	   if(Info.BIH.biCompression == BI_JPEG)
	   {
             if(Header_bfOffBits>0)
               fseek(f,Header_bfOffBits,SEEK_SET);
             else
               fseek(f,Info.BIH.biSize-40,SEEK_CUR);	// Try to guess data chunk start.
	     Raster = LoadFragmentJPG(f);
	   }
#endif
#if SupportPNG>=4 || SupportPNG==2
	   if(Info.BIH.biCompression == BI_PNG)
	   {
             if(Header_bfOffBits>0)
               fseek(f,Header_bfOffBits,SEEK_SET);
             else
               fseek(f,Info.BIH.biSize-40,SEEK_CUR);	// Try to guess data chunk start.
	     LoadFragmentPNG(f,&Img,0);
             return;		// Raster & Palette are already assigned, exit function.
	   }
#endif
	   if(Info.BIH.biCompression!=BI_ALPHABITFIELDS && Info.BIH.biCompression>BI_BITFIELDS)
		goto ENDPROC;		//unsupported compression mode(s)

	   k = Info.BIH.biPlanes*Info.BIH.biBitCount;
           if(Info.BIH.biSize==64)
           {
	     fseek(f,Info.BIH.biSize-56,SEEK_CUR);	// Gobble OS/2 header.
           }
           else if(Info.BIH.biSize>=52)
           {
	     Rd_dword(f,&Info.BIHv4.bV4RedMask);
             Rd_dword(f,&Info.BIHv4.bV4GreenMask);
	     Rd_dword(f,&Info.BIHv4.bV4BlueMask);
	     if(Info.BIH.biSize>=56)
             {
	       Rd_dword(f,&Info.BIHv4.bV4AlphaMask);
               if(Info.BIH.biSize>56) fseek(f,Info.BIH.biSize-56,SEEK_CUR);	// Gobble unreaded part of header.
             }
	     else
	         Info.BIHv4.bV4AlphaMask=0;
             //if(Info.BIH.biCompression!=BI_ALPHABITFIELDS) Info.BIHv4.bV4AlphaMask=0;

             if(Compression==BI_BITFIELDS)
             {
               if(k==32 &&
	        Info.BIHv4.bV4RedMask==0xFF && Info.BIHv4.bV4GreenMask==0xFF00 && Info.BIHv4.bV4BlueMask==0xFF0000 && Info.BIHv4.bV4AlphaMask==0xFF000000)
	       {
                 Compression=BI_RGB;	// Standard bit split for 32bpp.
               } else if(k==16 &&
	          Info.BIHv4.bV4RedMask==0x1F && Info.BIHv4.bV4GreenMask==0x3E0 && Info.BIHv4.bV4BlueMask==0xF800 && Info.BIHv4.bV4AlphaMask==0x0)
	       {
                 Compression=BI_RGB;	// Standard bit split for 16bpp.
               }
             }
           }
           else		// Cleanup all bitmask to zero default value.
           {
             Info.BIHv4.bV4RedMask = Info.BIHv4.bV4GreenMask = Info.BIHv4.bV4BlueMask = Info.BIHv4.bV4AlphaMask = 0x0;
             if(Info.BIH.biSize==40 && (Compression==BI_BITFIELDS || Compression==BI_ALPHABITFIELDS))
             {		// Explore a gap between header and data.
               i = Header_bfOffBits - ftell(f);
               if(i>=12)
               {
                 Rd_dword(f,&Info.BIHv4.bV4RedMask);
                 Rd_dword(f,&Info.BIHv4.bV4GreenMask);
                 Rd_dword(f,&Info.BIHv4.bV4BlueMask);
                 if(i>=16 && Compression==BI_ALPHABITFIELDS)
                 {
                   Rd_dword(f,&Info.BIHv4.bV4AlphaMask);
                 }
               }
             }
           }

	   }

           if(Info.BIH.biBitCount==64 && Info.BIH.biPlanes==1)
           {
             Raster = RasterRGB = CreateRaster2DRGBA(Info.BIH.biWidth,Info.BIH.biHeight,16);
           }
           else if(Info.BIH.biBitCount==32 && Info.BIH.biPlanes==1)
           {
             if(Compression==BI_RGB ||
                (((Compression==BI_BITFIELDS && Info.BIH.biSize>=56) || Compression==BI_ALPHABITFIELDS) && Info.BIHv4.bV4AlphaMask!=0 &&
                (Info.BIHv4.bV4RedMask!=0 || Info.BIHv4.bV4GreenMask!=0 || Info.BIHv4.bV4BlueMask!=0) ))
             {
               Raster = RasterRGB = CreateRaster2DRGBA(Info.BIH.biWidth,Info.BIH.biHeight,8);
               if(((Info.BIHv4.bV4BlueMask==0xFF && Info.BIHv4.bV4GreenMask==0xFF00 && Info.BIHv4.bV4RedMask==0xFF0000) ||
                   (Info.BIHv4.bV4RedMask==0 && Info.BIHv4.bV4GreenMask==0 && Info.BIHv4.bV4BlueMask==0))
                  && (Info.BIHv4.bV4AlphaMask==0 || Info.BIHv4.bV4AlphaMask==0xFF000000))
               {
                 RasterRGB = NULL;				// Classical order cloaked as bitfields.
               }
             }
             else	// RGB32 without alpha.
             {
               Raster = RasterRGB = CreateRaster2DRGB(Info.BIH.biWidth,Info.BIH.biHeight,8);
               if(Info.BIHv4.bV4RedMask==0 && Info.BIHv4.bV4GreenMask==0 && Info.BIHv4.bV4BlueMask==0)
               {
                 Info.BIHv4.bV4BlueMask=0xFF00;
                 Info.BIHv4.bV4GreenMask=0xFF;
                 Info.BIHv4.bV4RedMask=0xFF0000;
               }
               Info.BIHv4.bV4AlphaMask = 0;
             }
           }
           else if(Info.BIH.biBitCount==24 && Info.BIH.biPlanes==1)
             Raster = CreateRaster2DRGB(Info.BIH.biWidth,labs(Info.BIH.biHeight),8);
           else if(Info.BIH.biBitCount==16 && Info.BIH.biPlanes==1)
           {
             if((Compression==BI_BITFIELDS || Compression==BI_ALPHABITFIELDS) &&
                (Info.BIHv4.bV4RedMask!=0 || Info.BIHv4.bV4GreenMask!=0 || Info.BIHv4.bV4BlueMask!=0))
             {
               if(Info.BIHv4.bV4RedMask==0xFFFF && Info.BIHv4.bV4GreenMask==0xFFFF && Info.BIHv4.bV4BlueMask==0xFFFF && Info.BIHv4.bV4AlphaMask==0)
               {
                 Raster = CreateRaster2D(Info.BIH.biWidth,labs(Info.BIH.biHeight),16);	// Consider this to be 16 bit gray
               }
               else
                 Raster = RasterRGB = CreateRaster2DRGB(Info.BIH.biWidth,labs(Info.BIH.biHeight),8);
             }
             else
               Raster = CreateRaster2DRGB(Info.BIH.biWidth,labs(Info.BIH.biHeight),5);	// Use plain read pass.
           }
           else if(Info.BIH.biBitCount==8 && Info.BIH.biPlanes==1 && (Compression==BI_BITFIELDS || Compression==BI_ALPHABITFIELDS))
           {
             Raster = RasterRGB = CreateRaster2DRGB(Info.BIH.biWidth,labs(Info.BIH.biHeight),8);
           }
           else Raster = CreateRaster2D(Info.BIH.biWidth,labs(Info.BIH.biHeight),k);
           ldblk = Info.BIH.biClrUsed;
	   break;

	default: goto ENDPROC;
	}

 if(Raster==NULL) goto ENDPROC;				//unsupported format
 i = Raster->GetPlanes();
 if(!(i==1 || i==2 || i==4 || i==8 || i==16 || i==24 || i==32 || i==64))
	 {
ERASEEND:delete Raster;
	 Raster=NULL;
	 goto ENDPROC;
	 }

 if(i==1 || i==2 || i==4 || i==8) 				//load of the palette
 {
   Palette = BuildPalette(1<<i, 8);
   if(Palette!=NULL)
   {
     w = ((unsigned)1 << Raster->GetPlanes());
     if(ldblk==0) 
         ldblk = 1 << Raster->GetPlanes();
     else
     {
       if(w>ldblk) w=ldblk;
     }

     if(Info.BIH.biSize==12)
     {			// Here is only RGB record.
       for(i=0; i<w; i++)
       {
         fread(&PalItem,3,1,f);
         Palette->setR(i,PalItem.rgbRed);
         Palette->setG(i,PalItem.rgbGreen);
         Palette->setB(i,PalItem.rgbBlue);
       }
     }
     else		// Later BMP has RGBA
     {
       //i = ftell(f);
       for(i=0; i<w; i++)
       {
         fread(&PalItem,sizeof(PalItem),1,f);
         Palette->setR(i,PalItem.rgbRed);
         Palette->setG(i,PalItem.rgbGreen);
         Palette->setB(i,PalItem.rgbBlue);
       }
     }

     while(w<Palette->Size1D)	// Feed a rest of palette with some data.
     {
       Palette->setR(w,w);
       Palette->setG(w,w);
       Palette->setB(w,w);
       w++;
     }

     if(GrayPalette(Palette,Raster->GetPlanes()))
     {
       delete Palette;
       Palette = NULL;
     }
   }
 }

 if(Header_bfOffBits>0)
   fseek(f,Header_bfOffBits,SEEK_SET);
 if(RasterRGB==NULL)
   ldblk = (Raster->GetPlanes()*Raster->GetSize1D()+7)/8;
 else
   ldblk = (Info.BIH.biBitCount*Raster->GetSize1D()+7)/8;

	// padding
 k = -(int)ldblk & 0x3;	// 0->0; 1->3; 2->2; 3->1; 4->0;

 switch(Compression)
   {
   case BI_ALPHABITFIELDS:
   case BI_BITFIELDS:
         //i = ftell(f);
         if(RasterRGB!=NULL)
         {
           unsigned x,y;
           uint32_t pixel;
           RGBQuad RGB;
           TBitScaller SclRed, SclGreen, SclBlue, SclAlpha;
	   CalcScaller32(SclRed,Info.BIHv4.bV4RedMask);
	   CalcScaller32(SclGreen,Info.BIHv4.bV4GreenMask);
	   CalcScaller32(SclBlue,Info.BIHv4.bV4BlueMask);
	   CalcScaller32(SclAlpha,Info.BIHv4.bV4AlphaMask);

           y = RasterRGB->Size2D;
           w = 32 - 8;			// RGB quad supports 8 bits only. TODO: fix for bigger depth.
           while(y-->0)
           {
             if(feof(f)) goto ERASEEND;
             Raster1DAbstractRGB *RasterRGBrow = RasterRGB->GetRowRasterRGB(y);
             if(RasterRGBrow==NULL) break;
             for(x=0; x<RasterRGB->GetSize1D(); x++)
             {
               switch(Info.BIH.biBitCount)
               {
                 case 8: pixel=fgetc(f); break;
                 case 16: Rd_word(f,&i); pixel=i; break;
	                 case 32: Rd_dword(f,&pixel); break;
               }
               RGB.R = (((pixel&Info.BIHv4.bV4RedMask)>>SclRed.StartBit) * SclRed.Multiplier) >> w;
               RGB.G = (((pixel&Info.BIHv4.bV4GreenMask)>>SclGreen.StartBit) * SclGreen.Multiplier) >> w;
               RGB.B = (((pixel&Info.BIHv4.bV4BlueMask)>>SclBlue.StartBit) * SclBlue.Multiplier) >> w;
               RGB.O = (((pixel&Info.BIHv4.bV4AlphaMask)>>SclAlpha.StartBit) * SclAlpha.Multiplier) >> w;
               RasterRGBrow->Set(x,&RGB);
             }
             fread(&pixel,k,1,f);			//read till end of line
           }
           break;
         }					// Fall down for known rasters.
   case BI_RGB: i = Raster->Size2D;
                while(i-->0)
		{
		  if(fread(Raster->GetRow(i),ldblk,1,f)!=1)
			{break; /*goto ERASEEND;*/}
                  fseek(f,k,SEEK_CUR);		//read till end of line
 //		  AlineProc(i,p);
		}
		if(Raster->GetPlanes()==64)
		{
		  i = Raster->Size2D;
		  while(i-->0)
		  {
		    uint16_t *pix = (uint16_t *)Raster->GetRow(i);
		    uint16_t *pix2 = pix;
                    unsigned x = RasterRGB->GetSize1D() * 4;
                    while(x-- > 0)
		    {
		      if(*pix>=8192)
		        *pix = 65535;
                      else
			*pix <<= 3;
		      pix++;
                    }
                    RGBA64_BGRiA64((unsigned char*)pix2, RasterRGB->GetSize1D());
                  }
		}
		if(Raster->GetPlanes()==32)
		{
		  i = Raster->Size2D;
		  while(i-->0)
		      RGBA32_BGRiA32((unsigned char *)Raster->GetRow(i),Raster->GetSize1D());
		}
		if(Raster->GetPlanes()==24)
		{
		   i = Raster->Size2D;
		   while(i-->0)
		       RGB_BGR((char *)Raster->GetRow(i),Raster->GetSize1D());
		}
	  break;
   case BI_RLE8:{	// https://www.fileformat.info/format/bmp/spec/e27073c25463436f8a64fa789c886d9c/view.htm
	  unsigned x,y;
	  unsigned char *ptrB;

	  y = Raster->Size2D;
	  x = 0;
	  while( y-->0 )
	    {
	    ptrB = (unsigned char *)Raster->GetRow(y);
	    if(!ptrB) goto End_of_bitmap;
	    while(!feof(f))
	    {
	      Rd_word(f,(uint16_t*)&i);
	      //printf("\n%u %u %Xh",x,y,i);

	      if((i&0xFF)==0) 		//escape code
	      {
	        k = i >> 8;		// i = i>>8 does not work, a garbage is feed to upper BYTE in MSVC release, debug is OK.
		//printf(" >>%Xh",k);
		if(k==0)		// End of line.
		{
		  if(x<ldblk)
		  {
		    //if(y>=Raster->Size2D-1)
		      while(x++<ldblk) *ptrB++=0;  // cleanup till end of line
		    //else
		    //  memcpy(ptrB, x+(unsigned char *)Raster->GetRow(y+1), ldblk-x);
		  }
		  x = 0;
		  break;
		}
		if(k==1)		// End of bitmap.
		{
		  while(x++<ldblk) *ptrB++=0;  // cleanup till end of line
		  goto End_of_bitmap;
		}
		if(k==2)		// Delta.
		 {
		   Rd_word(f,(uint16_t*)&i);
		   x += i&0xFF;
		   y -= (i>>8)-1;
		   break;
		}

		while( (k>0)&&(x<ldblk) )
		{
		  Rd_word(f,(uint16_t*)&i);
		  //printf("\n REP:%u %u %Xh",x,y,i);
		  *ptrB = i;
		  ptrB++;
		  x++;
		  k--;
		  if((x==ldblk)|| k==0)
		    break;
		  *ptrB = i >> 8;
		  ptrB++;
		  x++;
		  k--;
		}
	      continue;
	     }
             else
	     {
		if(x>=ldblk)
			 continue;
		if((i&0xFF)>0)
			{
			k = i & 0xFF;
			if((unsigned)k>(ldblk-x))
				k = ldblk-x;		// Line overflow, should not occur.
			memset(ptrB, i>>8, k);
			ptrB+=k;
			x+=k;
			i-=k;
			}
		}
	     }
//	   if AlineProc<>nil then AlineProc^.NextLine;
	   if(feof(f)) goto End_of_bitmap;	//unexpected end
	   }
	break;
	}

   case BI_RLE4:{		//BI_RLE4
	 unsigned x,y;

	 y = Raster->Size2D;
	 x = 0;
	 while(y-->0)
	   {
	   while(!(feof(f)))
		{
		Rd_word(f,(uint16_t*)&i);
		if((i&0xFF)==0)			//escape code
			{
			k = i>>8;			// i = i>>8 does not work, a garbage is feed to upper BYTE in MSVC release, debug is OK.
			if(k==0) {x=0;break;} 	//end of line
			if(k==1) goto End_of_bitmap;
			if(k==2)
			     {
			     Rd_word(f,(uint16_t*)&i);
			     x+=i&0xFF;
			     y-=(i>>8)-1;
			     break;
			     }

			i = k;
			k = 0;
			ldblk=0;
			while( i>0 && x<Raster->GetSize1D() )
			    {
			    switch(k)
				{
				case 0:Rd_word(f,&ldblk);
				       Raster->SetValue2D(x,y,(ldblk >> 4)& 0xF);
				       break;
				case 1:Raster->SetValue2D(x,y,ldblk & 0xF);break;
				case 2:Raster->SetValue2D(x,y,ldblk >> 12);break;
				case 3:Raster->SetValue2D(x,y,(ldblk >> 8) & 0xF);break;
				}
			    x++;
			    i--;
			    k=(k+1) & 3;
			    }
			continue;
			}

		if((i&0xFF)>0)
			{
			ldblk=Raster->GetSize1D()-x;
			if(ldblk>(i&0xFF)) ldblk=(i&0xFF);
			k = i>>8;
			for(i=0;i<ldblk;i++)
				{
				if(i&1) Raster->SetValue2D(x,y,k & 0xF);
				   else Raster->SetValue2D(x,y,k >> 4);
				x++;
				}
			}
		}
//	   if AlineProc<>nil then AlineProc^.NextLine;
	   if(feof(f)) goto End_of_bitmap;	//unexpected end
	   }
     break;
     }

   }

End_of_bitmap:
  if(Info.BIH.biSize>=40 && Info.BIH.biHeight<0)
  {
    Flip2D(Raster);
  }
  //RasterRGB = NULL;

ENDPROC: 
 Img.AttachRaster(Raster);
 Img.AttachPalette(Palette);
}
#endif


#if SupportBMP>=3 || SupportDIB>=3

inline long SaveBmpHeader(FILE *f, const BMPHeader &SU)
{
#if defined(__PackedStructures__)
  return(fwrite(&SU,1,sizeof(SU),f));
#else
  return(savestruct(f,"a2dwwd",
	 SU.bfType, SU.bfSize, SU.bfReserved1,
	 SU.bfReserved2, SU.bfOffBits));
#endif
}

inline long SaveBmpInfHdr(FILE *f, const BitmapV4Header &SU)
{
#if defined(__PackedStructures__)
 return(fwrite(&SU.bV4Width,1,(SU.bV4Size==40)?40:56,f));
#else
 int ret = savestruct(f,"dddwwdddddd", SU.bV4Size,
	 SU.bV4Width, SU.bV4Height, SU.bV4Planes,
	 SU.bV4BitCount, SU.bV4Compression, SU.bV4SizeImage, SU.bV4XPelsPerMeter,
	 SU.bV4YPelsPerMeter, SU.bV4ClrUsed, SU.bV4ClrImportant);
 if(SU.bV4Size>=56)
     ret += savestruct(f,"dddd", SU.bV4RedMask, SU.bV4GreenMask, SU.bV4BlueMask, SU.bV4AlphaMask);
 return ret;
#endif
}


static int StoreContentBMP(FILE *f, const Image *CurrImg, BMPInfo_Coreinfo &Info, unsigned  ldblk, size_t StartDataPos, Raster1DAbstract *ConvertMe)
{
char k;
int i;
  
  Info.BIH.biWidth = CurrImg->Raster->GetSize1D();
  Info.BIH.biHeight = CurrImg->Raster->Size2D;
  Info.BIH.biPlanes = 1;
  //Info.BIH.biBitCount = StoredPlanes;
  //Info.BIH.biSizeImage = Header.bfSize-Header.bfOffBits;		//needs to be set before call
  if(Info.BIH.biBitCount==4 || Info.BIH.biBitCount==8) Info.BIH.biClrUsed=1 << (Info.BIH.biBitCount);

  SaveBmpInfHdr(f,Info.BIHv4);
 
  if(Info.BIH.biBitCount==1 || Info.BIH.biBitCount==4 || Info.BIH.biBitCount==8)
  {                                        //palette
     BMP_RGBQuad PalItem;
     PalItem.rgbReserved = 0;

     if(CurrImg->Palette==NULL)
     {
       const int PaletteSz = (1<<Info.BIH.biBitCount) - 1;
       for(i=0; i<=PaletteSz ;i++)
       {
	   PalItem.rgbBlue =
		PalItem.rgbGreen =
		PalItem.rgbRed = i*255 / PaletteSz;
	   fwrite(&PalItem,sizeof(PalItem),1,f);
        }
     }
     else
     {
	 k = CurrImg->Palette->GetPlanes()/CurrImg->Palette->Channels() - 8;
	 if(k<0) k=0;
	 for(int i=0; i<(1<<Info.BIH.biBitCount) ;i++)
	   {
	   PalItem.rgbRed = CurrImg->Palette->R(i)>>k;		// p.palette^.pal[K].Red;
	   PalItem.rgbGreen = CurrImg->Palette->G(i)>>k;	// p.palette^.pal[K].Green;
	   PalItem.rgbBlue = CurrImg->Palette->B(i)>>k;		// p.palette^.pal[K].Blue;
	   fwrite(&PalItem,sizeof(PalItem),1,f);
	   }
     }
   }

   fseek(f, StartDataPos, SEEK_SET);		// StartFramePos+Header.bfOffBits
   k = (-(int)ldblk)& 3;

   StartDataPos = 0;
   if(ConvertMe != NULL)
   {
     for(i=CurrImg->Raster->Size2D-1; i>=0; i--)
     {
       ConvertMe->Set(*CurrImg->Raster->GetRowRaster(i));
       if(ConvertMe->GetPlanes()==24)
	   RGB_BGR((char *)ConvertMe->Data1D, ConvertMe->Size1D);
       fwrite(ConvertMe->Data1D,ldblk,1,f);
       fwrite(&StartDataPos,k,1,f);	//write zeroes to end of line
     }
   }
   else
   {
     if(Info.BIH.biBitCount==24)
     {
       ConvertMe = CreateRaster1DRGB(CurrImg->Raster->GetSize1D(),8);
       if(ConvertMe==NULL || ConvertMe->Data1D==NULL) {delete ConvertMe;return -1;}
     }
     
     for(i=CurrImg->Raster->Size2D-1; i>=0; i--)
     {
       if(ConvertMe)
       {
         RGB_BGR2((unsigned char*)ConvertMe->Data1D, (unsigned char*)CurrImg->Raster->GetRow(i), CurrImg->Raster->GetSize1D());
         fwrite(ConvertMe->Data1D,ldblk,1,f);
       }
       else
       {		// Direct support.	  
         fwrite(CurrImg->Raster->GetRow(i),ldblk,1,f);	 
       }
       fwrite(&StartDataPos,k,1,f);     //write zeroes to end of line
//     AlineProc(i,p);    
    }
    if(ConvertMe!=NULL)
       delete ConvertMe;
  }

return 0;
}


#if SupportDIB>=3
int SavePictureDIB(const char *Name, const Image &Img)
{
FILE *f;
unsigned  ldblk;
BMPInfo_Coreinfo Info;
char StoredPlanes;
Raster1DAbstract *ConvertMe=NULL;

 if(Img.Raster==NULL) return(ErrEmptyRaster);

 if((f=fopen(Name,"wb"))==NULL) return(ErrOpenFile);

 
   StoredPlanes = Img.Raster->GetPlanes();
   if(StoredPlanes==2)		// BMP does not support 2 planes (WinCE only) - provide conversion.
   {
     StoredPlanes = 4;
     if(Img.ImageType()==ImageGray)
         CreateRaster1D(Img.Raster->GetSize1D(), StoredPlanes);
     else
     {
       ConvertMe = new Raster1D_4BitIDX;
       ConvertMe->Allocate1D(Img.Raster->GetSize1D());
     }
   }

   memset(&Info,0,sizeof(Info));
   Info.BIH.biSize = 40;
   Info.BIH.biCompression = 0;                      // Compression is not allowed.

   switch(Img.ImageType())
   {
     case ImageGray:
        if(StoredPlanes==16)
        {
          Info.BIH.biSize = 56;
          Info.BIHv4.bV4RedMask = Info.BIHv4.bV4GreenMask = Info.BIHv4.bV4BlueMask = 0xFFFF;
          //Info.BIHv4.bV4AlphaMask=0;
          Info.BIHv4.bV4Compression = BI_BITFIELDS;
        } else if(StoredPlanes==32)		//StoredPlanes==16 || 
        {
          StoredPlanes = 8;	//32 bit Gray images needs to be scalled
          ConvertMe = CreateRaster1D(Img.Raster->GetSize1D(), StoredPlanes);
        }
        break;
     case ImageTrueColor:
        if(StoredPlanes==32 || StoredPlanes==64)
        {
          StoredPlanes = 24;
          ConvertMe = CreateRaster1DRGB(Img.Raster->GetSize1D(),8);
        }
        break;
     case ImagePalette:
        if(StoredPlanes==16)	// BMP does not support 16bit palette.
        {
          StoredPlanes = 24;
          ConvertMe = CreateRaster1DRGB(Img.Raster->GetSize1D(),8);
        }
        break;
   }

   ldblk = (StoredPlanes*Img.Raster->GetSize1D()+7)/8;

   Info.BIH.biBitCount = StoredPlanes;
   Info.BIH.biSizeImage = Img.Raster->Size2D*((ldblk+3) & 0xFFFC);

   if(StoredPlanes==1 || StoredPlanes==4 || StoredPlanes==8)	// Palette is arbitrary for planes <=8     
     StoreContentBMP(f, &Img, Info, ldblk, Info.BIH.biSize+(1 << (StoredPlanes + 2)), ConvertMe);
   else
     StoreContentBMP(f, &Img, Info, ldblk, Info.BIH.biSize, ConvertMe);

   if(ConvertMe) {delete(ConvertMe);ConvertMe=NULL;}    

//   StartFramePos = ftell(f);
// } while(CurrImg!=NULL);

 fclose(f);
 return(0);
}
#endif


#if SupportBMP>=3
int SavePictureBMP(const char *Name, const Image &Img)
{
FILE *f;
unsigned  ldblk;
BMPHeader Header;
BMPInfo_Coreinfo Info;
char StoredPlanes;
Raster1DAbstract *ConvertMe=NULL;
long StartFramePos=0;
const Image *CurrImg = &Img;

 if(Img.Raster==NULL) return(ErrEmptyRaster);

 if((f=fopen(Name,"wb"))==NULL) return(ErrOpenFile);

 do
 {
   StoredPlanes = CurrImg->Raster->GetPlanes();
   if(StoredPlanes==2)		// BMP does not support 2 planes (WinCE only) - provide conversion.
   {
     StoredPlanes = 4;
     if(CurrImg->ImageType()==ImageGray)
         CreateRaster1D(CurrImg->Raster->GetSize1D(), StoredPlanes);
     else
     {
       ConvertMe = new Raster1D_4BitIDX;
       ConvertMe->Allocate1D(CurrImg->Raster->GetSize1D());
     }
   }

   memset(&Info,0,sizeof(Info));
   Info.BIH.biSize = 40;
   Info.BIH.biCompression = 0;                      // Compression is not allowed.

   switch(CurrImg->ImageType())
   {
     case ImageGray:
        if(StoredPlanes==16)
        {
          Info.BIH.biSize = 56;
          Info.BIHv4.bV4RedMask = Info.BIHv4.bV4GreenMask = Info.BIHv4.bV4BlueMask = 0xFFFF;
          //Info.BIHv4.bV4AlphaMask=0;
          Info.BIHv4.bV4Compression = BI_BITFIELDS;
        } else if(labs(StoredPlanes)==32 || labs(StoredPlanes)==64)		//StoredPlanes==16 || 
        {
          StoredPlanes = 8;	//32 bit Gray images needs to be scalled
          ConvertMe = CreateRaster1D(CurrImg->Raster->GetSize1D(), StoredPlanes);
        }
        break;
     case ImageTrueColor:
        if(CurrImg->Raster->Channels()==3 || CurrImg->Raster->Channels()==4 || StoredPlanes==32 || StoredPlanes==64)
        {
          StoredPlanes = 24;
          ConvertMe = CreateRaster1DRGB(CurrImg->Raster->GetSize1D(),8);
        }
        break;
     case ImagePalette:
        if(StoredPlanes==16)	// BMP does not support 16bit palette.
        {
          StoredPlanes = 24;
          ConvertMe = CreateRaster1DRGB(CurrImg->Raster->GetSize1D(),8);
        }
        break;
   }

   ldblk = (labs(StoredPlanes)*CurrImg->Raster->GetSize1D()+7)/8;

   memset(&Header,0,sizeof(Header));
   memmove(&Header.bfType,"BM",2);
#if defined(__PackedStructures__)
   Header.bfOffBits = sizeof(Header)+Info.BIH.biSize;
#else
   Header.bfOffBits = 14 + Info.BIH.biSize;
#endif
   if(StoredPlanes==1 || StoredPlanes==4 || StoredPlanes==8)	// Palette is arbitrary for planes <=8
     Header.bfOffBits+=(1 << (StoredPlanes + 2));
   Header.bfSize = Header.bfOffBits + CurrImg->Raster->Size2D*((ldblk+3) & 0xFFFC);

   SaveBmpHeader(f,Header);

   Info.BIH.biBitCount = StoredPlanes;
   Info.BIH.biSizeImage = Header.bfSize-Header.bfOffBits;

   StoreContentBMP(f, CurrImg, Info, ldblk, StartFramePos+Header.bfOffBits, ConvertMe);

   if(ConvertMe) {delete(ConvertMe);ConvertMe=NULL;}    

   CurrImg = CurrImg->Next;
   StartFramePos = ftell(f);
 } while(CurrImg!=NULL);

 fclose(f);
 return(0);
}
#endif

#endif

#endif
//-------------------End of BMP routines------------------


#if defined(SupportICO) || defined(SupportCUR)

#ifdef __PACKING__
 #pragma pack(push, 1)
#endif

typedef struct
{
    uint16_t idReserved;
    uint16_t idType;
    uint16_t idCount;
} IconHeader;

#ifdef __PACKING__
 #pragma pack(pop)
#endif


inline long LoadHeaderICO(FILE *f, IconHeader &HdrIco)
{
#if defined(__PackedStructures__)
 return(fread(&HdrIco,1,sizeof(IconHeader),f));
#else
 return(loadstruct(f,"www",
	 &HdrIco.idReserved, &HdrIco.idType, &HdrIco.idCount));
#endif
}


#endif


//--------------------------CUR---------------------------
#ifdef SupportCUR

#ifdef __PACKING__
 #pragma pack(push, 1)
#endif

typedef struct
{
	uint8_t  bWidth;
	uint8_t  bHeight;
	uint8_t  bColorCount;		///< Number of Colors (2,16, 0=256)
	uint8_t  bReserved;		///< =0
	uint16_t XHotspot;		///< =1
	uint16_t YHotspot;
	uint32_t dwBytesInRes;
	uint32_t dwImageOffset;
} CurDirectoryEntry;

#ifdef __PACKING__
 #pragma pack(pop)
#endif

#if SupportCUR>=4 || SupportCUR==2

inline long LoadCurDirectoryEntry(FILE *f, CurDirectoryEntry &IDEn)
{
#if defined(__PackedStructures__)
 return(fread(&IDEn,1,sizeof(IconDirectoryEntry),f));
#else
 return(loadstruct(f,"bbbbwwdd",
	 &IDEn.bWidth,&IDEn.bHeight,&IDEn.bColorCount,&IDEn.bReserved,
	 &IDEn.XHotspot, &IDEn.YHotspot, &IDEn.dwBytesInRes, &IDEn.dwImageOffset));
#endif
}


Image LoadPictureCUR(const char *Name)
{
FILE  *f;
uint16_t ldblk;
uint16_t i;
uint16_t k;
uint16_t IterEntry;
Image Img;
Image *CurrImg = &Img;
IconHeader Header;
CurDirectoryEntry Entry;
BMPInfo_Coreinfo Info;
BMP_RGBQuad RGB;

  if((f=fopen(Name,"rb"))==NULL) return(Img);
  if(LoadHeaderICO(f,Header)!=6) goto ENDPROC;

  if(Header.idReserved!=0 || Header.idType!=2 || Header.idCount<1)
  {
//	LoadPictureICO:=ErrAnother;
    goto ENDPROC;
  }

  for(IterEntry=0; IterEntry<Header.idCount; IterEntry++)
  {
    fseek(f,6+16*IterEntry, SEEK_SET);
    if(feof(f)) continue;

    k = LoadCurDirectoryEntry(f,Entry);
    if(k!=16) continue;

    fseek(f,Entry.dwImageOffset,SEEK_SET);

    if(RdDWORD_LoEnd(&Info.BIH.biSize,f)!=4) continue;
    switch(Info.BIH.biSize)
    {
      case 12:	//OS/2 V1  BITMAPCOREHEADER  OS/2 and also all Windows versions since Windows 3.0
	   {
	   if(LoadPartBmpCoreInfHdr(f,Info.BCIH) != (12-4)) continue;
	   if(Info.BCIH.bcPlanes!=1)  goto ENDPROC;
	   k = Info.BCIH.bcPlanes * Info.BCIH.bcBitCount;
           if(Info.BCIH.bcWidth!=Entry.bWidth) // then {or(Info.biHeight<>Entry.bHeight)}
	   {
//	     LoadPictureICO:=ErrAnother;
	     goto ENDPROC;
	   }
	   break;
	   }

	case 108:	//Windows V4 	BITMAPV4HEADER 	all Windows versions since Windows 95/NT4
        case 124:	//Windows V5 	BITMAPV5HEADER 	Windows 98/2000 and newer
				// fallback; sometimes  this occurs
	case 40:	//Windows V3  BITMAPINFOHEADER  all Windows versions since Windows 3.0
	case 52:	// BITMAPV2INFOHEADER
	case 56:	// BITMAPV3INFOHEADER
        case 78:
        case 64:	//OS/2 V2  BITMAPCOREHEADER2
	   {
	   if(LoadPartBmpInfHdr(f,Info.BIH) != (40-4)) continue;
	   if(Info.BIH.biPlanes!=1) goto ENDPROC;
           k = Info.BIH.biPlanes*Info.BIH.biBitCount;
           if(Info.BIH.biWidth!=Entry.bWidth) // then {or(Info.biHeight<>Entry.bHeight)}
	   {
             if(Info.BIH.biWidth!=256 && Entry.bWidth!=0)
             {
//	       LoadPictureICO:=ErrAnother;
	       goto ENDPROC;
             }
	   }
	   if(Info.BIH.biSize > 40) fseek(f, Info.BIH.biSize-40, SEEK_CUR);
	   break;
           }

        default: continue;
  }
  //if(fread(&Info,sizeof(Info),1,f)!=1) goto ENDPROC;

/*
 if(Entry.bColorCount==0) Entry.bColorCount=255;
 i = (int)(log((float)Entry.bColorCount)/log(2.0));
 if(i==3) i=4;
 if(i>=5 && i<=7) i=8;
*/

 if(!CurrImg->isEmpty())
 {
   CurrImg->Next = new Image;
   CurrImg = CurrImg->Next;
 }

 CurrImg->AttachRaster(CreateRaster2D(Info.BIH.biWidth, Info.BIH.biHeight/2, k));
 if(CurrImg->Raster==NULL) goto ENDPROC;
// if AlineProc<>nil then AlineProc^.InitPassing(p.y,'Loading ICO');

 if(k <= 8)
 {
   CurrImg->AttachPalette(BuildPalette(1<<k,8));
   if(CurrImg->Palette!=NULL)
   {
     if(Entry.bColorCount<255) Entry.bColorCount--;
     if(Entry.bColorCount>2 && Entry.bColorCount<15) Entry.bColorCount=15;
     for(i=0; i<1<<k; i++)
     {
	fread(&RGB,sizeof(RGB),1,f);
	CurrImg->Palette->setR(i,RGB.rgbRed);
	CurrImg->Palette->setG(i,RGB.rgbGreen);
	CurrImg->Palette->setB(i,RGB.rgbBlue);
     }
     if(GrayPalette(CurrImg->Palette,CurrImg->Raster->GetPlanes()))
     {
       CurrImg->AttachPalette(NULL);
     }
   }
 }

 {
   PropertyItem *Prop = new PropertyItem("Hotspot",2*sizeof(unsigned));
   if(Prop)
     if(Prop->Data)
     {
        *(unsigned*)Prop->Data = Entry.XHotspot;
        ((unsigned*)Prop->Data)[1] = Entry.YHotspot;
        CurrImg->AttachProperty(Prop);
        Prop = NULL;
     }
     else
         {delete Prop;Prop=NULL;}
 }

  ldblk = (CurrImg->Raster->GetPlanes()*CurrImg->Raster->GetSize1D()+7)/8; //XOR part
  k = (-(int)ldblk)& 3;
  i = CurrImg->Raster->Size2D;
  while(i>0)
  {
    i--;
    if(fread(CurrImg->Raster->GetRow(i),ldblk,1,f)!=1)
    {
      // Vyplnit rastr nìèím.
      break;
    }
    if(k>0) fseek(f,k,SEEK_CUR);
    //	AlineProc(i,p);
  }

	//!!!!!!!!!!!		{AND part is omitted!}
 }

ENDPROC:
 fclose(f);
 return(Img);
}
#endif




#endif
//-------------------End of CUR routines------------------


//--------------------------ICO---------------------------
#ifdef SupportICO

#ifdef __PACKING__
 #pragma pack(push, 1)
#endif

typedef struct
	{
	uint8_t  bWidth;
	uint8_t  bHeight;
	uint8_t  bColorCount;		///< Number of Colors (2,16, 0=256)
	uint8_t  bReserved;		///< =0
	uint16_t wPlanes;		///< =1
	uint16_t wBitCount;
	uint32_t dwBytesInRes;
	uint32_t dwImageOffset;
	} IconDirectoryEntry;

#ifdef __PACKING__
 #pragma pack(pop)
#endif


#if SupportICO>=4 || SupportICO==2

inline long LoadIconDirectoryEntry(FILE *f, IconDirectoryEntry &IDEn)
{
#if defined(__PackedStructures__)
 return(fread(&IDEn,1,sizeof(IconDirectoryEntry),f));
#else
 return(loadstruct(f,"bbbbwwdd",
	 &IDEn.bWidth,&IDEn.bHeight,&IDEn.bColorCount,&IDEn.bReserved,
	 &IDEn.wPlanes, &IDEn.wBitCount, &IDEn.dwBytesInRes, &IDEn.dwImageOffset));
#endif
}

Image LoadPictureICO(const char *Name)
{
FILE  *f;
uint16_t ldblk;
uint16_t i;
uint16_t k;
uint16_t IterEntry;
Image Img;
Image *CurrImg = &Img;
IconHeader Header;
IconDirectoryEntry Entry;
BMPInfo_Coreinfo Info;
BMP_RGBQuad RGB;

  if((f=fopen(Name,"rb"))==NULL) return(Img);
  if(LoadHeaderICO(f,Header)!=6) goto ENDPROC;

  if(Header.idReserved!=0 || Header.idType!=1 || Header.idCount<1)
  {
//	LoadPictureICO:=ErrAnother;
    goto ENDPROC;
  }

  for(IterEntry=0; IterEntry<Header.idCount; IterEntry++)
  {
    fseek(f,6+16*IterEntry, SEEK_SET);
    if(feof(f)) continue;

    k = LoadIconDirectoryEntry(f,Entry);
    if(k!=16) continue;

    fseek(f,Entry.dwImageOffset,SEEK_SET);

    if(RdDWORD_LoEnd(&Info.BIH.biSize,f)!=4) continue;
    switch(Info.BIH.biSize)
    {
      case 12:	//OS/2 V1  BITMAPCOREHEADER  OS/2 and also all Windows versions since Windows 3.0
	   {
	   if(LoadPartBmpCoreInfHdr(f,Info.BCIH) != (12-4)) continue;
	   if(Info.BCIH.bcPlanes!=1)  goto ENDPROC;
	   k = Info.BCIH.bcPlanes * Info.BCIH.bcBitCount;
           if(Info.BCIH.bcWidth!=Entry.bWidth) // then {or(Info.biHeight<>Entry.bHeight)}
	   {
//	     LoadPictureICO:=ErrAnother;
	     goto ENDPROC;
	   }
	   break;
	   }

	case 108:	//Windows V4 	BITMAPV4HEADER 	all Windows versions since Windows 95/NT4
        case 124:	//Windows V5 	BITMAPV5HEADER 	Windows 98/2000 and newer
				// fallback; sometimes  this occurs
	case 40:	//Windows V3  BITMAPINFOHEADER  all Windows versions since Windows 3.0
	case 52:	// BITMAPV2INFOHEADER
	case 56:	// BITMAPV3INFOHEADER
        case 78:
        case 64:	//OS/2 V2  BITMAPCOREHEADER2
	   {
	   if(LoadPartBmpInfHdr(f,Info.BIH) != (40-4)) continue;
	   if(Info.BIH.biPlanes!=1) goto ENDPROC;
           k = Info.BIH.biPlanes*Info.BIH.biBitCount;
           if(Info.BIH.biWidth!=Entry.bWidth) // then {or(Info.biHeight<>Entry.bHeight)}
	   {
             if(Info.BIH.biWidth!=256 && Entry.bWidth!=0)
             {
//	       LoadPictureICO:=ErrAnother;
	       goto ENDPROC;
             }
	   }
	   if(Info.BIH.biSize > 40) fseek(f, Info.BIH.biSize-40, SEEK_CUR);
	   break;
           }

        default: continue;
  }
  //if(fread(&Info,sizeof(Info),1,f)!=1) goto ENDPROC;

/*
 if(Entry.bColorCount==0) Entry.bColorCount=255;
 i = (int)(log((float)Entry.bColorCount)/log(2.0));
 if(i==3) i=4;
 if(i>=5 && i<=7) i=8;
*/

 if(!CurrImg->isEmpty())
 {
   CurrImg->Next = new Image;
   CurrImg = CurrImg->Next;
 }

 CurrImg->AttachRaster(CreateRaster2D(Info.BIH.biWidth, Info.BIH.biHeight/2, k));
 if(CurrImg->Raster==NULL) goto ENDPROC;
// if AlineProc<>nil then AlineProc^.InitPassing(p.y,'Loading ICO');

 if(k <= 8)
 {
   CurrImg->AttachPalette(BuildPalette(1<<k,8));
   if(CurrImg->Palette!=NULL)
   {
     for(i=0; i<1<<k; i++)
     {
	if(fread(&RGB,1,sizeof(RGB),f) != 4) goto ENDPROC;
	CurrImg->Palette->setR(i,RGB.rgbRed);
	CurrImg->Palette->setG(i,RGB.rgbGreen);
	CurrImg->Palette->setB(i,RGB.rgbBlue);
     }
     if(GrayPalette(CurrImg->Palette,CurrImg->Raster->GetPlanes()))
     {
       CurrImg->AttachPalette(NULL);
     }
   }
 }

 ldblk = (CurrImg->Raster->GetPlanes()*CurrImg->Raster->GetSize1D()+7)/8; //XOR part
 k = (-(int)ldblk)& 3;
 i = CurrImg->Raster->Size2D;
 while(i>0)
 {
    i--;
    if(fread(CurrImg->Raster->GetRow(i),ldblk,1,f)!=1)
    {
      // Vyplnit rastr nìèím.
      break;
    }
    if(k>0) fseek(f,k,SEEK_CUR);
    //	AlineProc(i,p);
  }

	//!!!!!!!!!!!		{AND part is omitted!}
 }

ENDPROC:
 fclose(f);
 return(Img);
}
#endif


#if SupportICO>=3

inline long SaveIconDirectoryEntry(FILE *f, IconDirectoryEntry &IDEn)
{
#if defined(__PackedStructures__)
 return(fwrite(&IDEn,1,sizeof(IconDirectoryEntry),f));
#else
 return(savestruct(f,"bbbbwwdd",
	 IDEn.bWidth,IDEn.bHeight,IDEn.bColorCount,IDEn.bReserved,
	 IDEn.wPlanes, IDEn.wBitCount, IDEn.dwBytesInRes, IDEn.dwImageOffset));
#endif
}

inline long SaveIcoHeader(FILE *f, const IconHeader &IHDR)
{
#if defined(__PackedStructures__)
  return(fwrite(&IHDR,1,sizeof(IHDR),f));
#else
  return(savestruct(f,"www",
	 IHDR.idReserved, IHDR.idType, IHDR.idCount));
#endif
}


int SavePictureICO(const char *Name, const Image &Img)
{
FILE *f;
uint16_t Ldblk1,Ldblk;
int i,k;
IconHeader Header;
IconDirectoryEntry Entry;
BMPInfo_Coreinfo Info;
BMP_RGBQuad RGB;
void *Dummy;

 if(Img.Raster==NULL || Img.Raster->Data2D==NULL) return(ErrEmptyRaster);
 if(Img.Raster->GetPlanes()==16 || Img.Raster->GetPlanes()>24 ||
    Img.Raster->GetSize1D()>256 || Img.Raster->Size2D>256)
	return(-9); //image is too big to fit into the icon

 if((f=fopen(Name,"wb"))==NULL) return(ErrOpenFile);

 Header.idReserved=0;
 Header.idType=1;
 Header.idCount=1;

 if(SaveIcoHeader(f,Header)!=6) goto FINISH;

 Ldblk = ((long)Img.Raster->GetPlanes()*Img.Raster->GetSize1D()+7) / 8;
 Ldblk1 = ((long)Img.Raster->GetSize1D()+7) / 8;

 Entry.bWidth = Img.Raster->GetSize1D();
 Entry.bHeight = Img.Raster->Size2D;
 switch(Img.Raster->GetPlanes())
 {
   case 1: Entry.bColorCount=2; break;
   case 4: Entry.bColorCount=16; break;
   default: Entry.bColorCount=0; break;		// for 8bpp and RGB use 0
 } 
 Entry.bReserved = 0;
 Entry.wPlanes = 0;
 Entry.wBitCount = Img.Raster->GetPlanes();
 Entry.dwImageOffset = 16 + 6;	//sizeof(Entry) + sizeof(Header);
 Entry.dwBytesInRes = 40 + Img.Raster->Size2D*(((Ldblk+3)&0xFFFC)+((Ldblk1+3)&0xFFFC));
 if(Img.Raster->GetPlanes()<=8)
   Entry.dwBytesInRes += (1 << Img.Raster->GetPlanes())*sizeof(RGB);

 if(SaveIconDirectoryEntry(f,Entry) != 16) goto FINISH;

 Info.BIH.biSize = 40;
 Info.BIH.biWidth = Img.Raster->GetSize1D();
 Info.BIH.biHeight = 2*Img.Raster->Size2D;
 Info.BIH.biPlanes = 1;
 Info.BIH.biBitCount = Img.Raster->GetPlanes();
 Info.BIH.biCompression = 0;
 Info.BIH.biSizeImage = Img.Raster->GetSize2D()*(((Ldblk+3)&0xFFFC));
 Info.BIH.biXPelsPerMeter = 0;
 Info.BIH.biYPelsPerMeter = 0;
 Info.BIH.biClrImportant = Info.BIH.biClrUsed = (Img.Raster->Channels()>=3) ? 0 : ( (1 << Img.Raster->GetPlanes()));

 if(SaveBmpInfHdr(f,Info.BIHv4)!=40) goto FINISH;

// if AlineProc<>nil then AlineProc^.InitPassing(2*p.y,'Saving ICO');

 if((k=Img.Raster->GetPlanes())<=8)
 {
   APalette *Palette = Img.Palette;
   if(Palette==NULL)
   {
     Palette = BuildPalette(1<<k,8);
     if(Palette)
     {
       if(Palette->Data1D==NULL)
           {delete(Palette);Palette=NULL;}
       else 
           FillGray(Palette);
     }
   }
   if(Palette!=NULL)
   {
     k = Palette->GetPlanes() / Palette->Channels();
     k -= 8;
     if(k<0) k=0;
     for(i=0; i<(1 << Img.Raster->GetPlanes()); i++)
     {
       RGB.rgbRed = Palette->R(i)>>k;	//  p.palette^.pal[K].Red;
       RGB.rgbGreen = Palette->G(i)>>k;	// p.palette^.pal[K].Green;
       RGB.rgbBlue = Palette->B(i)>>k;	// p.palette^.pal[K].Blue;
       fwrite(&RGB,sizeof(RGB),1,f);
     }
     if(Palette!=Img.Palette) delete Palette;
   }
 }

 k = (-(int)Ldblk)& 3;
 for(i=Entry.bHeight-1; i>=0; i--)	//XOR part
 {
   fwrite(Img.Raster->GetRow(i),Ldblk,1,f);
   // if AlineProc<>nil then AlineProc^.NextLine;
   fwrite(&Header,k,1,f);		// write padding, use this wariable to provide dummy data.
 }

 k = (-(int)Ldblk1)& 3;
 Ldblk1 += k;			// append padding
 Dummy = calloc(Ldblk1,1);		// calloc() provides zero fill
 for(i=Entry.bHeight-1;i>=0;i--)	//AND part is empty
 {
   fwrite(Dummy,Ldblk1,1,f);
	//if AlineProc<>nil then AlineProc^.NextLine;
 }
 free(Dummy);

FINISH:
  fclose(f);
  return(0);
}	//SaveICO
#endif

#endif
//-------------------End of ICO routines------------------


//--------------------------WMF----------------------------
#if SupportWMF>0

#ifdef __PACKING__
 #pragma pack(push, 1)
#endif

struct STRETCHDIB_PARAMS
   {
   uint32_t bmp_draw_type; 		//0,1
   uint16_t  ColorUsage;			//2
   uint16_t bmp_draw_crop_h;		//3
   uint16_t bmp_draw_crop_w;		//4
   uint16_t bmp_draw_crop_y;		//5
   uint16_t bmp_draw_crop_x;		//6
   int16_t par_U16_h;			//7
   int16_t par_U16_w;			//8
   int16_t par_U16_y;			//9
   int16_t par_U16_x;			//10
   };

struct DIBSTRETCHBLT_PARAMS
   {
   uint32_t RasterOperation;
   int16_t SrcHeight;
   int16_t SrcWidth;
   int16_t YSrc;
   int16_t XSrc;
   int16_t DestHeight;
   int16_t DestWidth;
   int16_t YDest;
   int16_t XDest;
   };


struct DIBBITBLT_PARAMS
   {
   uint32_t RasterOperation;
   int16_t YSrc;
   int16_t XSrc;
   int16_t Height;
   int16_t Width;
   int16_t YDest;
   int16_t XDest;
   };

typedef struct _WindowsMetaHeader
{
  uint16_t  FileType;       /* Type of metafile (0=memory, 1=disk) */
  uint16_t  HeaderSize;     /* Size of header in WORDS (always 9) */
  uint16_t  Version;        /* Version of Microsoft Windows used */
  uint32_t FileSize;       /* Total size of the metafile in WORDs */
  uint16_t  NumOfObjects;   /* Number of objects in the file */
  uint32_t MaxRecordSize;  /* The size of largest record in WORDs */
  uint16_t  NumOfParams;    /* Not Used (always 0) */
} WMFHEAD;


typedef struct _StandardMetaRecord
{
    uint32_t Size;          /* Total size of the record in WORDs */
    uint16_t  Function;      /* Function number (defined in WINDOWS.H) */
    uint32_t ParamFilePos;
    //uint16_t  Parameters[];  /* Parameter values passed to function */
} WMFRECORD;


typedef struct _WmfSpecialHeader
{
  uint32_t Key;           /* Magic number (always 9AC6CDD7h) */
  uint16_t  Handle;        /* Metafile HANDLE number (always 0) */
  int16_t Left;          /* Left coordinate in metafile units */
  int16_t Top;           /* Top coordinate in metafile units */
  int16_t Right;         /* Right coordinate in metafile units */
  int16_t Bottom;        /* Bottom coordinate in metafile units */
  uint16_t  Inch;          /* Number of metafile units per inch */
  uint32_t Reserved;      /* Reserved (always 0) */
  uint16_t  Checksum;      /* Checksum value for previous 10 WORDs */
} WMFSPECIAL;

typedef struct _EnhancedMetaHeader
{
    uint32_t   RecordType;       /* Record type */
    uint32_t   RecordSize;       /* Size of the record in bytes */
    int32_t  BoundsLeft;       /* Left inclusive bounds */
    int32_t  BoundsRight;      /* Right inclusive bounds */
    int32_t  BoundsTop;        /* Top inclusive bounds */
    int32_t  BoundsBottom;     /* Bottom inclusive bounds */
    int32_t  FrameLeft;        /* Left side of inclusive picture frame */
    int32_t  FrameRight;       /* Right side of inclusive picture frame */
    int32_t  FrameTop;         /* Top side of inclusive picture frame */
    int32_t  FrameBottom;      /* Bottom side of inclusive picture frame */
    uint32_t   Signature;        /* Signature ID (always 0x464D4520) */
    uint32_t   Version;          /* Version of the metafile */
    uint32_t   Size;             /* Size of the metafile in bytes */
    uint32_t   NumOfRecords;     /* Number of records in the metafile */
    uint16_t    NumOfHandles;     /* Number of handles in the handle table */
    uint16_t    Reserved;         /* Not used (always 0) */
    uint32_t   SizeOfDescrip;    /* Size of description string in WORDs */
    uint32_t   OffsOfDescrip;    /* Offset of description string in metafile */
    uint32_t   NumPalEntries;    /* Number of color palette entries */
    int32_t  WidthDevPixels;   /* Width of reference device in pixels */
    int32_t  HeightDevPixels;  /* Height of reference device in pixels */
    int32_t  WidthDevMM;       /* Width of reference device in millimeters */
    int32_t  HeightDevMM;      /* Height of reference device in millimeters */
} ENHANCEDMETAHEADER;

#ifdef __PACKING__
 #pragma pack(pop)
#endif


static inline long LoadWMFHeader(FILE *f, _WindowsMetaHeader & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,1,sizeof(_WindowsMetaHeader),f));
#else
return(loadstruct(f,"wwwdwdw",&SU.FileType,&SU.HeaderSize,
	&SU.Version,&SU.FileSize,&SU.NumOfObjects,&SU.MaxRecordSize,&SU.NumOfParams));
#endif
}


static inline long LoadWMFSpecial(FILE *f, _WmfSpecialHeader & WSH)
{
#if defined(__PackedStructures__)
return(fread(&WSH,1,sizeof(_WmfSpecialHeader),f));
#else
return(loadstruct(f,"dwwwwwwdw",
      &WSH.Key, &WSH.Handle,
      &WSH.Left, &WSH.Top, &WSH.Right, &WSH.Bottom,
      &WSH.Inch, &WSH.Reserved, &WSH.Checksum));
#endif
}


static inline _StandardMetaRecord *LoadWmfRecord(FILE *f)
{
uint32_t Size;
_StandardMetaRecord *WmfRec;

  Rd_dword(f,&Size);
  if(Size<3 || feof(f)) return NULL;
  WmfRec=(_StandardMetaRecord *)malloc(sizeof(_StandardMetaRecord));
  WmfRec->Size=Size;
  Rd_word(f,&WmfRec->Function);
  WmfRec->ParamFilePos = ftell(f);
/*
  for(Size=0;Size<WmfRec->Size-3;Size++)
    {
    Rd_word(f,&WmfRec->Parameters[Size]);
    }
*/
  return(WmfRec);
}


static inline long LoadWMF_DIB_PARAMS(FILE *f, STRETCHDIB_PARAMS & params)
{
#if defined(__PackedStructures__)
return(fread(&params,1,sizeof(STRETCHDIB_PARAMS),f));
#else
return(loadstruct(f,"Dwwwwwwwww",
	 &params.bmp_draw_type,
	 &params.ColorUsage,
	 &params.bmp_draw_crop_h, &params.bmp_draw_crop_w,
	 &params.bmp_draw_crop_y, &params.bmp_draw_crop_x,
	 &params.par_U16_h, &params.par_U16_w,
	 &params.par_U16_y, &params.par_U16_x));
#endif
}


static inline long LoadWMF_DIBSTRETCHBLT_PARAMS(FILE *f, DIBSTRETCHBLT_PARAMS & params)
{
#if defined(__PackedStructures__)
return(fread(&params,1,sizeof(DIBSTRETCHBLT_PARAMS),f));
#else
return(loadstruct(f,"Dwwwwwwww",
	&params.RasterOperation,
	&params.SrcHeight, &params.SrcWidth,
	&params.YSrc, &params.XSrc,
	&params.DestHeight, &params.DestWidth,
	&params.YDest, &params.XDest));
#endif
}


static inline long LoadWMF_DIBBITBLT_PARAMS(FILE *f, DIBBITBLT_PARAMS & params)
{
#if defined(__PackedStructures__)
return(fread(&params,1,sizeof(DIBBITBLT_PARAMS),f));
#else
return(loadstruct(f,"Dwwwwww",
	&params.RasterOperation,
	&params.YSrc, &params.XSrc,
	&params.Height, &params.Width,
	&params.YDest, &params.XDest));
#endif
}


/** This function extracts bitmap from a WMF stream */
void WMF_STRETCHDIB(Image &Img, FILE *f, uint32_t ParamFilePos)
{
STRETCHDIB_PARAMS params;
BitmapInfoHeader BIH;

  fseek(f,ParamFilePos,SEEK_SET);
  if(LoadWMF_DIB_PARAMS(f,params)!=22) return;

  if(RdDWORD_LoEnd(&BIH.biSize,f)!=4) return;

  if(BIH.biSize==40)
    {
    if(LoadPartBmpInfHdr(f,BIH) != (40-4)) return;

    fseek(f,ParamFilePos+22,SEEK_SET);
    if(BIH.biPlanes*BIH.biBitCount == 1)
      LoadBmpStream(Img,f,ParamFilePos+22+BIH.biSize+8); //skip 8bit palette for monochromatic images
    else
      {
      /*if(BIH.biCompression==BI_RGB)
        LoadBmpStream(Img,f,ParamFilePos+22+BIH.biSize+1);
      else*/
        LoadBmpStream(Img,f,0);
      }
    }
  else	// don't know
    {
    fseek(f,ParamFilePos+2*11,SEEK_SET);
    LoadBmpStream(Img,f,0);
    }

  Img.x = params.par_U16_x;
  Img.y = params.par_U16_y;
  Img.dx = params.par_U16_w;
  Img.dy = params.par_U16_h;
}


/** This function extracts bitmap from a WMF stream */
void WMF_DIBSTRETCHBLT(Image &Img, FILE *f, uint32_t ParamFilePos)
{
DIBSTRETCHBLT_PARAMS params;
BitmapInfoHeader BIH;

  fseek(f,ParamFilePos,SEEK_SET);

  if(LoadWMF_DIBSTRETCHBLT_PARAMS(f, params)!=20) return;

  if(RdDWORD_LoEnd(&BIH.biSize,f)!=4) return;

  if(BIH.biSize==40)
    {
    if(LoadPartBmpInfHdr(f,BIH) != (40-4)) return;

    fseek(f,ParamFilePos+20,SEEK_SET);
    if(BIH.biPlanes*BIH.biBitCount == 1)
      LoadBmpStream(Img,f,ParamFilePos+20+BIH.biSize+8); //skip 8bit palette for monochromatic images
    else
      {
      /*if(BIH.biCompression==BI_RGB)
        LoadBmpStream(Img,f,ParamFilePos+22+BIH.biSize+1);
      else*/
        LoadBmpStream(Img,f,0);
      }
    }
  else	// don't know
    {
    fseek(f,ParamFilePos+20,SEEK_SET);
    LoadBmpStream(Img,f,0);
    }

	// Upper left corner is at [XDest, YDest]
  Img.x = params.XDest;
  Img.y = params.YDest - params.DestHeight;
  Img.dx = params.DestWidth;
  Img.dy = params.DestHeight;
}


/** This function extracts bitmap from a WMF stream */
void WMF_DIBBITBLT(Image &Img, FILE *f, uint32_t ParamFilePos)
{
DIBBITBLT_PARAMS params;
BitmapInfoHeader BIH;

  fseek(f,ParamFilePos,SEEK_SET);

  if(LoadWMF_DIBBITBLT_PARAMS(f, params)!=16) return;

  if(RdDWORD_LoEnd(&BIH.biSize,f)!=4) return;

  if(BIH.biSize==40)
    {
    if(LoadPartBmpInfHdr(f,BIH) != (40-4)) return;

    fseek(f,ParamFilePos+16,SEEK_SET);
    if(BIH.biPlanes*BIH.biBitCount == 1)
      LoadBmpStream(Img,f,ParamFilePos+16+BIH.biSize+8); //skip 8bit palette for monochromatic images
    else
      {
      /*if(BIH.biCompression==BI_RGB)
        LoadBmpStream(Img,f,ParamFilePos+22+BIH.biSize+1);
      else*/
        LoadBmpStream(Img,f,0);
      }
    }
  else	// don't know
    {
    fseek(f,ParamFilePos+20,SEEK_SET);
    LoadBmpStream(Img,f,0);
    }

  Img.x = params.XDest;
  Img.y = params.YDest;
  Img.dx = params.Width;
  Img.dy = params.Height;
}


#if SupportWMF>=4 || SupportWMF==2
Image LoadPictureWMF(const char *Name)
{
_WindowsMetaHeader WmfHead;
_StandardMetaRecord *WmfRec;
int Rec;
uint32_t NewPos, ActualPos;
FILE *f;
Image Img;

  if((f=fopen(Name,"rb"))==NULL) return(Img);

  if(LoadWMFHeader(f,WmfHead)!=18)
    {fclose(f);return(Img);}
  if(WmfHead.HeaderSize!=9)
    {fclose(f);return(Img);}

  //perc.Init(ftell(wpd), fsize,_("First pass WMF:") );

  ActualPos = ftell(f);
  Rec = 0;
  while(!feof(f))
    {
    WmfRec=LoadWmfRecord(f);
    if(WmfRec!=NULL)
      {
      NewPos=0;

      switch(WmfRec->Function)
	{
/*
	case 0x0000: strcpy(ObjType,"WMF End"); break;

	case 0x001E: strcpy(ObjType,"!SaveDC"); break;

	case 0x0035: strcpy(ObjType,"!RealizePalette"); break;
	case 0x0037: strcpy(ObjType,"!SetPalEntries"); break;

	case 0x004F: strcpy(ObjType,"!StartPage"); break;
        case 0x0050: strcpy(ObjType,"!EndPage"); break;

	case 0x0052: strcpy(ObjType,"!AbortDoc"); break;
        case 0x005E: strcpy(ObjType,"!EndDoc"); break;

	case 0x00F7: strcpy(ObjType,"!CreatePalette"); break;
	case 0x00F8: strcpy(ObjType,"!CreateBrush"); break;

	case 0x0102: strcpy(ObjType,"!SetBkMode"); break;
	case 0x0103: strcpy(ObjType,"!SetMapMode"); break;
	case 0x0104: strcpy(ObjType,"!SetROP2"); break;
	case 0x0105: strcpy(ObjType,"!SetRelabs"); break;
	case 0x0106: strcpy(ObjType,"!SetPolyFillMode"); break;
	case 0x0107: strcpy(ObjType,"!SetStretchBltMode"); break;
	case 0x0108: strcpy(ObjType,"!SetTextCharExtra"); break;

	case 0x0127: strcpy(ObjType,"!RestoreDC"); break;
	case 0x012A: strcpy(ObjType,"!InvertRegion"); break;
	case 0x012B: strcpy(ObjType,"!PaintRegion"); break;
	case 0x012C: strcpy(ObjType,"!SelectClipRegion"); break;
	case 0x012D: strcpy(ObjType,"!SelectObject"); break;
	case 0x012E: strcpy(ObjType,"!SetTextAlign"); break;

	case 0x0139: strcpy(ObjType,"!ResizePalette"); break;

	case 0x0142: strcpy(ObjType,"!DibCreatePatternBrush"); break;

	case 0x014C: strcpy(ObjType,"!ResetDc"); break;
	case 0x014D: strcpy(ObjType,"!StartDoc"); break;

	case 0x01F0: strcpy(ObjType,"!DeleteObject"); break;

	case 0x01F9: strcpy(ObjType,"!CreatePatternBrush"); break;

	case 0x0201: strcpy(ObjType,"!SetBkColor"); break;

	case 0x0209: strcpy(ObjType,"!SetTextColor"); break;
	case 0x020A: strcpy(ObjType,"!SetTextJustification"); break;
	case 0x020B: strcpy(ObjType,"!SetWindowOrg"); break;
	case 0x020C: strcpy(ObjType,"!SetWindowExt"); break;
	case 0x020D: strcpy(ObjType,"!SetViewportOrg"); break;
	case 0x020E: strcpy(ObjType,"!SetViewportExt"); break;
	case 0x020F: strcpy(ObjType,"!OffsetWindowOrg"); break;

	case 0x0211: strcpy(ObjType,"!OffsetViewportOrg"); break;
	case 0x0213: strcpy(ObjType,"!LineTo"); break;
	case 0x0214: strcpy(ObjType,"!MoveTo"); break;

	case 0x0220: strcpy(ObjType,"!OffsetClipRgn"); break;

	case 0x0228: strcpy(ObjType,"!FillRegion"); break;

	case 0x0231: strcpy(ObjType,"!SetMapperFlags"); break;

	case 0x0234: strcpy(ObjType,"!SelectPalette"); break;

	case 0x02FA: strcpy(ObjType,"!CreatePenIndirect"); break;
	case 0x02FB: strcpy(ObjType,"!CreateFontIndirect"); break;
	case 0x02FC: strcpy(ObjType,"!CreateBrushIndirect"); break;
	case 0x02FD: strcpy(ObjType,"!CreateBitmapIndirect"); break;

	case 0x0324: strcpy(ObjType,"!Polygon"); break;
	case 0x0325: strcpy(ObjType,"!Polyline"); break;

	case 0x0410: strcpy(ObjType,"!ScaleWindowExt"); break;

	case 0x0412: strcpy(ObjType,"!ScaleViewportExt"); break;

	case 0x0415: strcpy(ObjType,"!ExcludeClipRect"); break;
	case 0x0416: strcpy(ObjType,"!IntersectClipRect"); break;

	case 0x0418: strcpy(ObjType,"!Ellipse"); break;
	case 0x0419: strcpy(ObjType,"!FloodFill"); break;

	case 0x041B: strcpy(ObjType,"!Rectangle"); break;

	case 0x041F: strcpy(ObjType,"!SetPixel"); break;

	case 0x0429: strcpy(ObjType,"!FrameRegion"); break;

	case 0x0436: strcpy(ObjType,"!AnimatePalette"); break;

	case 0x0521: strcpy(ObjType,"!TextOut"); break;

	case 0x0538: strcpy(ObjType,"!PolyPolygon"); break;

	case 0x0548: strcpy(ObjType,"!ExtFloodFill"); break;

	case 0x626:  strcpy(ObjType,"Escape");break;

	case 0x061C: strcpy(ObjType,"!RoundRect"); break;
	case 0x061D: strcpy(ObjType,"!PatBlt"); break;

	case 0x062F: strcpy(ObjType,"!DrawText"); break;

	case 0x06FE: strcpy(ObjType,"!CreateBitmap"); break;
	case 0x06FF: strcpy(ObjType,"!CreateRegion"); break;

        case 0x0817: strcpy(ObjType,"!Arc"); break;

	case 0x081A: strcpy(ObjType,"!Pie"); break;

 	case 0x0830: strcpy(ObjType,"!Chord"); break;

	case 0x0922: strcpy(ObjType,"!BitBlt"); break;
*/
	case 0x0940: WMF_DIBBITBLT(Img, f, WmfRec->ParamFilePos); break;
/*
	case 0x0A32: strcpy(ObjType,"!ExtTextOut"); break;

	case 0x0B23: strcpy(ObjType,"!StretchBlt"); break;
*/
	case 0x0B41: WMF_DIBSTRETCHBLT(Img, f, WmfRec->ParamFilePos);
		     //strcpy(ObjType,"!DibStretchBlt");
		     break;

//	case 0x0d33: strcpy(ObjType,"!SetDibToDev"); break;

	case 0x0F43: WMF_STRETCHDIB(Img, f, WmfRec->ParamFilePos);
//		     strcpy(ObjType,"StretchDIBits");
		     break;
	}


      if(NewPos!=0) ActualPos=NewPos;
	       else ActualPos+=2*WmfRec->Size;
      free(WmfRec); WmfRec=NULL;

      fseek(f,ActualPos,SEEK_SET);
      }
    else
      ActualPos=ftell(f);

    Rec++;
    }

  fclose(f);
  return(Img);
}
#endif

#endif
//-------------------End of WMF routines------------------

