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

#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define _USE_MATH_DEFINES
#include <math.h>
#if defined(_MSC_VER) || defined(__CYGWIN32__) || defined(__APPLE__) || defined(__MINGW32__)
 #define MAXLONG     0x7FFFFFFF
#else
 #include <values.h>
#endif
#ifndef M_PI
 #define M_PI        3.14159265358979323846
#endif
#include <time.h>

#include "typedfs.h"
#include "common.h"
#include "raster.h"
#include "vecimage.h"
#include "img_tool.h"
#include "ras_prot.h"
#include "std_str.h"
#include "img_futi.h"

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

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

/*
#if defined(HasZLIB)	// For TIFF images.
#  include <zlib.h>
#endif
*/

#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);

const char UnitName[] = "Rasters";
const char ExifPreffix[6] = {'E','x','i','f', '\0', '\0'};

#ifdef _MSC_VER
  #pragma warning(disable: 4244)
#endif


//------------------AAI--------------------------
#ifdef SupportAAI

#if SupportAAI>=4 || SupportAAI==2
Image LoadPictureAAI(const char *Name)
{
Raster2DAbstractRGBA *Raster=NULL;
size_t fSize;
size_t FrameStart=0;
Image Img, *CurrImg=&Img;
FILE  *f;
uint32_t k, ldblk;
uint16_t width, height;
unsigned i;
unsigned char c;

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

 while(!feof(f))
 {
   if(fSize<8+FrameStart) break;
   RdDWORD_LoEnd(&ldblk,f);
   if(ldblk > 0xFFFF) break;
   width = ldblk&0xFFFF;
   RdDWORD_LoEnd(&ldblk,f);
   if(ldblk > 0xFFFF) break;
   height = ldblk&0xFFFF;

   ldblk = 4*width;

   if(fSize<FrameStart+8+ldblk*height) break;
 
   Raster = CreateRaster2DRGBA(width,height,8);
   if(Raster==NULL) break;
// if AlineProc<>nil then AlineProc^.InitPassing(p.y,'Loading AAI');

   for(i=0; i<Raster->Size2D; i++)
   {
     unsigned char *pRow = (unsigned char *)Raster->GetRow(i);
     if(fread(pRow,ldblk,1,f)!=1) break;
     for(k=0; k<ldblk; k+=4)
     {
       c = pRow[k+3];
       if(c==254) c=255;
       pRow[k+3] = ~c;
       c = pRow[k]; pRow[k]=pRow[k+2]; pRow[k+2]=c;
     } 
   }
   
   if(CurrImg->Raster!=NULL)
   {
     CurrImg->Next = new Image;
     CurrImg = CurrImg->Next;
   }
   CurrImg->AttachRaster(Raster);
   Raster=NULL;
   FrameStart += 8 + ldblk*height;
 }

 fclose(f);
 return(Img);
}

#endif


#if SupportAAI>=3

int SavePictureAAI(const char *Name,const Image &Img)
{
FILE *f;
uint32_t k, ldblk;
uint16_t i;
char c;
Raster1D_8BitRGBA TempData;
const Image *CurrImg = &Img;

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

  while(CurrImg!=NULL)
  {
    if(CurrImg->Raster!=NULL)
    {
      TempData.Allocate1D(Img.Raster->GetSize1D());
      if(TempData.Data1D==NULL) {fclose(f);return(-11);}

      WrWORD_LoEnd(CurrImg->Raster->GetSize1D(),f);
      WrWORD_LoEnd(0,f);
      WrWORD_LoEnd(CurrImg->Raster->Size2D,f);
      WrWORD_LoEnd(0,f);

      ldblk = 4* CurrImg->Raster->GetSize1D();

      for(i=0; i<CurrImg->Raster->Size2D; i++)
      {
        TempData.Set(*CurrImg->Raster->GetRowRaster(i));
        for(k=0; k<ldblk; k+=4)
        {
          c = ~((unsigned char*)TempData.Data1D)[k+3];
          if(c==255) c=245;
          ((unsigned char*)TempData.Data1D)[k+3] = c;
          c = ((unsigned char*)TempData.Data1D)[k]; 
          ((unsigned char*)TempData.Data1D)[k] = ((unsigned char*)TempData.Data1D)[k+2];
          ((unsigned char*)TempData.Data1D)[k+2]=c;
        } 
        fwrite(TempData.Data1D,ldblk,1,f);
      }

      if(TempData.Data1D) TempData.Erase();
    }
    CurrImg = CurrImg->Next;
  }

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

#endif
//-------------------End of AAI routines------------------


//------------------ART--------------------------
#ifdef SupportART

#if SupportART>=4 || SupportART==2
Image LoadPictureART(const char *Name)
{
FILE  *f;
uint16_t ldblk;
uint16_t k,width,height;
unsigned i;
long dummy;
Image Img;

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

 RdWORD_LoEnd(&k,f);
 RdWORD_LoEnd(&width,f);
 RdWORD_LoEnd(&k,f);
 RdWORD_LoEnd(&height,f);

 ldblk = (width+7)/8;
 k = ldblk & 1;

 if(FileSize(f)!=8+((long)ldblk+k)*height)
 {
   goto ENDPROC;
 }
 Img.AttachRaster(CreateRaster2D(width,height,1));
 if(Img.isEmpty()) goto ENDPROC;
// if AlineProc<>nil then AlineProc^.InitPassing(p.y,'Loading ART');

 for(i=0; i<Img.Raster->Size2D; i++)
 {
   if(fread(Img.Raster->GetRow(i),ldblk,1,f)!=1) break;

   if(k>0)
     {if(fread(&dummy,k,1,f)!=k) break;}	//read line padding
   //	AlineProc(i,p);
 }

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

#endif


#if SupportART>=3

int SavePictureART(const char *Name,const Image &Img)
{
FILE *f;
uint16_t ldblk;
uint16_t i;
char k;
Raster1D_1Bit TempData;
static const char dummy = 0;

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

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

  WrWORD_LoEnd(0,f);
  WrWORD_LoEnd(Img.Raster->GetSize1D(),f);
  WrWORD_LoEnd(0,f);
  WrWORD_LoEnd(Img.Raster->Size2D,f);

  ldblk = (Img.Raster->GetSize1D()+7)/8;
  k = (-ldblk) & 1;

  if(Img.Raster->GetPlanes()!=1)
    {
    TempData.Allocate1D(Img.Raster->GetSize1D());
    }

  for(i=0; i<Img.Raster->Size2D; i++)
	{
	if(TempData.Data1D)
	  {
	  TempData.Set(*Img.Raster->GetRowRaster(i));
	  fwrite(TempData.Data1D,ldblk,1,f);
	  }
	else
	  fwrite(Img.Raster->GetRow(i),ldblk,1,f);
	fwrite(&dummy,k,1,f);			//write till end of line
	// if AlineProc<>nil then AlineProc^.NextLine;
	}

  if(TempData.Data1D) TempData.Erase();
  fclose(f);
  return(0);
}
#endif

#endif
//-------------------End of ART routines------------------


//------------------AVS--------------------------
#ifdef SupportAVS

#if SupportAVS>=4 || SupportAVS==2
Image LoadPictureAVS(const char *Name)
{
Raster2DAbstractRGBA *Raster=NULL;
size_t fSize;
size_t FrameStart=0;
Image Img, *CurrImg=&Img;
FILE  *f;
uint32_t k, ldblk;
uint16_t width, height;

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

 while(!feof(f))
 {
   if(fSize<8+FrameStart) break;
   RdDWORD_HiEnd(&ldblk,f);
   if(ldblk > 0xFFFF) break;
   width = ldblk&0xFFFF;
   RdDWORD_HiEnd(&ldblk,f);
   if(ldblk > 0xFFFF) break;
   height = ldblk&0xFFFF;

   ldblk = 4*width;

   if(fSize<FrameStart+8+ldblk*height) break;
 
   Raster = CreateRaster2DRGBA(width,height,8);
   if(Raster==NULL) break;
// if AlineProc<>nil then AlineProc^.InitPassing(p.y,'Loading AVS');

   for(unsigned y=0; y<Raster->Size2D; y++)
   {
     unsigned char *const pRow = (unsigned char *)Raster->GetRow(y);
     if(pRow==NULL) break;
     if(fread(pRow,ldblk,1,f)!=1) break;
     for(k=0; k<ldblk; k+=4)
     {
       const uint8_t c = pRow[k];
       pRow[k] = pRow[k+1];
       pRow[k+1] = pRow[k+2];
       pRow[k+2] = pRow[k+3];
       pRow[k+3] = ~c;
     } 
   }
   
   if(CurrImg->Raster!=NULL)
   {
     CurrImg->Next = new Image;
     CurrImg = CurrImg->Next;
   }
   CurrImg->AttachRaster(Raster);
   Raster=NULL;
   FrameStart += 8 + ldblk*height;
 }

 fclose(f);
 return(Img);
}

#endif


#if SupportAVS>=3

int SavePictureAVS(const char *Name,const Image &Img)
{
FILE *f;
uint32_t k, ldblk;
uint16_t i;
Raster1D_8BitRGBA TempData;
const Image *CurrImg = &Img;

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

  while(CurrImg!=NULL)
  {
    if(CurrImg->Raster!=NULL)
    {
      TempData.Allocate1D(Img.Raster->GetSize1D());
      if(TempData.Data1D==NULL) {fclose(f);return(-11);}

      WrWORD_HiEnd(0,f);
      WrWORD_HiEnd(CurrImg->Raster->GetSize1D(),f);
      WrWORD_HiEnd(0,f);
      WrWORD_HiEnd(CurrImg->Raster->Size2D,f);

      ldblk = 4* CurrImg->Raster->GetSize1D();

      for(i=0; i<CurrImg->Raster->Size2D; i++)
      {
        TempData.Set(*CurrImg->Raster->GetRowRaster(i));
        for(k=0; k<ldblk; k+=4)
        {
          const uint8_t c = ((uint8_t*)TempData.Data1D)[k+3];
          ((uint8_t*)TempData.Data1D)[k+3] = ((uint8_t*)TempData.Data1D)[k+2];
          ((uint8_t*)TempData.Data1D)[k+2] = ((uint8_t*)TempData.Data1D)[k+1];
          ((uint8_t*)TempData.Data1D)[k+1] = ((uint8_t*)TempData.Data1D)[k];          
          ((uint8_t*)TempData.Data1D)[k] = ~c;		//invert alpha
        }
        fwrite(TempData.Data1D,ldblk,1,f);
      }

      if(TempData.Data1D) TempData.Erase();
    }
    CurrImg = CurrImg->Next;
  }

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

#endif
//-------------------End of AVS routines------------------


//----------------CSV-comma separated values---------------
#ifdef SupportCSV


#if SupportCSV>=4 || SupportCSV==2

Image LoadPictureCSV(const char *Name)
{
FILE *f;
uint16_t x, y, R_X;
unsigned char ch;
long max, i;
Raster2DAbstract *Raster=NULL;
Image Img;
bool Doubles = false;
bool isValid;

  if((f=fopen(Name,"rt"))==NULL) return(Img);
  y = 0;
  max = 0;
  R_X = 0;

  ch = 0;
  while(!feof(f))	//auto detect sizes and num of planes
    {
    while(!isdigit(ch) && ch!='-')	// goto a beginning of a number
	 {             //go to the begin of number
	 ch = fgetc(f);
	 if(feof(f)) goto EndReading;
	 if(ch==0 || ch>128 || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z'))
		goto FINISH; 	//not a text data
	 }

    x = 0;
    do {
       if(!isdigit(ch) && ch!='-') ch=0;	// && ch!=0

       i = ReadInt(f,(char *)&ch,&isValid);
       if(i<0) Doubles = true;		//Use doubles for negative numbers.
       if(isValid) x++;
       if(ch=='.') 
         {
         ch = 0;
         if(ReadInt(f,(char *)&ch)!=0)	//dummy read frac part
           Doubles = true;
         }
       if(ch=='e')
         {
         ch = 0;
         ReadInt(f,(char *)&ch);
         Doubles = true;
         }       

       if(i>max) max=i;
       if(feof(f)) break;
       while(ch==' ') ch = fgetc(f);		//read spaces to next number
       if(ch==',')				// gobble comma that separate values
         {
         ch = fgetc(f);
         while(ch==' ') ch = fgetc(f);	//read spaces to next number
         }
       } while(!(ch==0 || ch==10 || ch==13 || ch==';' || ch==':' || ch>128 ||
		(ch>='a' && ch<='z') || (ch>='A' && ch<='Z')) );

    if(!feof(f))
      {
      if(ch==';') readln(f);		//comment
      if(ch==0 || ch==':' || ch>128 || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z'))
		goto FINISH; 		//not a text data
      }
    if(x>=1)
      {
      y++;
      if(x>R_X) R_X=x;
      }
    }
EndReading:
//  if IOResult!=0  goto Konec;
  fseek(f,0,SEEK_SET);

  if(Doubles)
    {
    Raster = CreateRaster2D(R_X,y,-64);
    }
  else
    {
    i=1;
    if(max>=    2) i=2;
    if(max>=    4) i=4;
    if(max>=   16) i=8;
    if(max>=  256) i=16;
    if(max>=65536) i=32;

    Raster = CreateRaster2D(R_X,y,i);
    }
  if(Raster==NULL) goto FINISH;
//if(AlineProc!=nil) AlineProc^.InitPassing(p.y,'Loading CSV');

  y = 0;
  ch = 0;
  while(!feof(f)) 	//load picture data
    {
    x = 0;
    while(!((ch >= '0' && ch <= '9') || ch=='-'))
	 {		//move to the beginning of number
	 if(feof(f)) goto FINISH;
	 ch = fgetc(f);
	 }
    do {
       if(Doubles)
         Raster->SetValue2Dd(x,y,ReadDouble(f,(char *)&ch));
       else
         Raster->SetValue2D(x,y,ReadInt(f,(char *)&ch));
       //if IOresult!=0  goto Konec;
       x++;
       if(ch=='.')
       {
         ch = 0;			// Toss away decimal dot.
         ReadInt(f,(char *)&ch);	// Dummy read frac part
       }
       if(feof(f)) break;
       while(ch==' ') ch=fgetc(f);		//read spaces to next number
       if(ch==',')				// gobble comma that separate values
         {
         ch = fgetc(f);
         while(ch==' ') ch = fgetc(f);	//read spaces to next number
         }
       } while(!(ch==0 || ch==10 || ch==13 || ch==';' ||  ch>128 || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z')) );

    if(ch==';') readln(f);
    if(x>=1) y++;
    }

FINISH:
  fclose(f);
  Img.AttachRaster(Raster);  
  return(Img);
}
#endif


#if SupportCSV>=3
int SavePictureCSV(const char *Name,const Image &Img)
{
FILE *f;
uint16_t i,j;

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

 if((f=fopen(Name,"wt"))==NULL) return(ErrOpenFile);
 //if AlineProc<>nil AlineProc^.InitPassing(p.y*p.planes,'Saving TXT');

 //printf("1D%d 2D%d",Img.Raster->GetSize1D(),Img.Raster->Size2D);
 for(i=0;i<Img.Raster->Size2D;i++)
   {
   if(Img.Raster->GetPlanes()>=0 && Img.Raster->GetPlanes()<=32)
     {
     fprintf(f,"%u",Img.GetPixel(0,i));
     for(j=1;j<Img.Raster->GetSize1D();j++)
	{
	fprintf(f,", %u",Img.GetPixel(j,i));
	}
     }
   else
     {
     fprintf(f,"%f",Img.Raster->GetValue2Dd(0,i));
     for(j=1;j<Img.Raster->GetSize1D();j++)
	{
	fprintf(f,", %f",Img.Raster->GetValue2Dd(j,i));
	}
     }
   fprintf(f,"\n");
   //if AlineProc<>nil then AlineProc^.NextLine;
   }

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

#endif
//-------------------End of CSV routines------------------


//--------------------------CUT----------------------------
#ifdef SupportCUT

struct RGBWord
	{
	uint16_t rgbRed;
	uint16_t rgbGreen;
	uint16_t rgbBlue;
	};

struct CUTHeader		//Dr Hallo
	{
	uint16_t	width;
	uint16_t	height;
	uint16_t	Reserved;
	};

struct CUTPalHeader
	{
	char 	FileId[2];
	uint16_t	Version;
	uint16_t	Size;
	uint8_t	FileType;
	uint8_t	SubType;
	uint16_t	BoardID;
	uint16_t	GraphicsMode;
	uint16_t	MaxIndex;
	uint16_t	MaxRed;
	uint16_t	MaxGreen;
	uint16_t	MaxBlue;
	char 	PaletteId[20];
       };


#if SupportCUT>=4 || SupportCUT==2
Image LoadPictureCUT(const char *Name)
{
FILE *f;
long ldblk;
uint16_t i,j;
uint8_t *ptrB;
CUTHeader Header;
CUTPalHeader PalHeader;
RGBWord Pal;
uint16_t EncodedByte;
uint8_t RunCount,RunCountMasked,RunValue;
Raster2DAbstract *Raster=NULL;
APalette *Palette=NULL;
Image Img;

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

 if(fread(&Header,sizeof(Header),1,f)!=1)
   goto Finish;
 if(Header.Reserved!=0 || Header.height==0 || Header.width==0)
   goto Finish;
/*---This code checks first line of image---*/
 Rd_word(f,&EncodedByte);
 RunCount=fgetc(f);
 RunCountMasked=RunCount & 0x7F;
 ldblk=0;
 while(RunCountMasked>0)	/*end of line?*/
	{
	i=1;
	if(RunCount<0x80) i=RunCountMasked;
	fseek(f,i,SEEK_CUR);
	if(feof(f)) goto Finish;	/*wrong data*/
	EncodedByte-=i+1;
	ldblk+=RunCountMasked;

	RunCount=fgetc(f);
	if(feof(f)) goto Finish;	/*wrong data: unexpected eof in line*/
	RunCountMasked=RunCount & 0x7F;
	}
 if(EncodedByte!=1) goto Finish;		/*wrong data: size incorrect*/
 i=0;				/*guess a number of bit planes*/
 if(ldblk==Header.width)   i=8;
 if(2*ldblk==Header.width) i=4;
 if(8*ldblk==Header.width) i=1;
 if(i==0) goto Finish;			/*wrong data: size incorrect*/
/*-----End of check-----*/
 Raster=CreateRaster2D(Header.width,Header.height,i);
 if(Raster==NULL) goto Finish;

 //if(AlineProc!=NULL) AlineProc->InitPassing(p.y,'Loading CUT');

 fseek(f,sizeof(Header),SEEK_SET);	/*Ldblk was set in the check phase*/

 for(i=0;i<Header.height;i++)
	{
	Rd_word(f,&EncodedByte);

	ptrB=(uint8_t *)Raster->GetRow(i);
	j=ldblk;

	RunCount=fgetc(f);
	RunCountMasked=RunCount & 0x7F;
	while(RunCountMasked>0)		/*end of line?*/
		{
		if(RunCountMasked>j)
		  {		/*Wrong Data*/
		  //asm int 3; end;
		  RunCountMasked=j;
		  if(j==0) break;
		  }

		if(RunCount>0x80)
		  {
		  RunValue=fgetc(f);
		  memset(ptrB,RunValue,RunCountMasked);
		  }
		else
		  if(fread(ptrB,RunCountMasked,1,f)!=1) {goto Finish;}

		ptrB+=RunCountMasked;
		j-=RunCountMasked;

		if(feof(f)) goto Finish;	/*wrong data: unexpected eof in line*/
		RunCount=fgetc(f);
		RunCountMasked = RunCount & 0x7F;
		}

	//if(AlineProc!=NULL)  AlineProc^.NextLine;
	}

/*Try to load a palette*/
 fclose(f); f=NULL;
 if(Raster!=NULL)		/*remains loading palette*/
	{
	char *NameBuffer;

	j=i=strlen(Name);
	NameBuffer=(char *)malloc(i+5);
	strcpy(NameBuffer,Name);

	while(--i>0)
	  {
	  if(Name[i]=='.') break;
	  if(Name[i]=='/' || Name[i]=='\\' || Name[i]==':')
	       {i=j;break;}
	  }
	strcpy(NameBuffer+i,".PAL");
	if((f=fopen(NameBuffer,"rb"))==NULL)
	     {
	     strcpy(NameBuffer+i,".pal");
	     if((f=fopen(NameBuffer,"rb"))==NULL)
		 {
		 NameBuffer[i]=0;
		 if((f=fopen(NameBuffer,"rb"))==NULL)
			goto PalFail;
		 }
	     }

	if(fread(&PalHeader,sizeof(PalHeader),1,f)!=1) /*introduction information*/
	  goto PalFail;
	if(PalHeader.FileId[0]!='A' || PalHeader.FileId[1]!='H')
	  goto PalFail;

	Palette = BuildPalette(1<<Raster->GetPlanes(),8); //depth 16 ??
	ldblk = (1<<Raster->GetPlanes()) - 1;
	if(PalHeader.MaxIndex<ldblk) ldblk=PalHeader.MaxIndex;
	for(i=0;i<ldblk;i++)
		{      /*this may be wrong- I don't know why is palette such strange*/
		j=ftell(f);
		if( (j%512)>512-sizeof(Pal) )
			{
			j=((j / 512)+1)*512;
			fseek(f,j,SEEK_SET);
			}
		fread(&Pal,sizeof(Pal),1,f);
		Palette->setR(i,Pal.rgbRed);
		Palette->setG(i,Pal.rgbGreen);
		Palette->setB(i,Pal.rgbBlue);
		}

PalFail:
	if(f) {fclose(f);f=NULL;}
	if(Palette!=NULL)
	  if(GrayPalette(Palette,Raster->GetPlanes()))
		{
		delete Palette;
		Palette=NULL;
		}
	if(NameBuffer) {free(NameBuffer);NameBuffer=NULL;}
	}

Finish:
 if(f) {fclose(f);f=NULL;}
 Img.AttachRaster(Raster); 
 Img.AttachPalette(Palette);
 return(Img);
}	/*LoadCUT*/

#endif


#endif
//-------------------End of CUT routines------------------


//--------------------------EPS----------------------------
#ifdef SupportEPS


#if SupportEPS>=4 || SupportEPS==2

Image LoadPictureEPS(const char *Name)
{
FILE *F;
uint16_t x,y,Class;
int planes;
char ch;
RGBQuad RGB;
unsigned long i;
char a[256];
uint8_t *ptrB;
bool RLE_Compressed;
Raster2DAbstract *Raster=NULL;
APalette *Palette=NULL;
Image Img;

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

 RLE_Compressed=false;

 ReadWord(F,a,sizeof(a));
 if(strncmp(a,"%!PS",4)) goto FINISH;
 readln(F);

 ch=0;
 while(!feof(F)) 		//auto detect sizes and num of planes
   {
   ReadWord(F,a,sizeof(a),&ch);
   if(*a==0) continue;

   if(*a=='%')			//strip comments
     {
     if(ch!='\n' && ch!='\r') readln(F);
     continue;
     }

    if(*a=='/')		//ignore definition
      {
      if(!strcmp(a,"/cmap"))
	{
	i = ftell(F);
	planes = ReadInt(F,&ch);
	if(planes>0 && planes/3<=256)
	  {
	  Palette = BuildPalette(planes/3,8);
	  }
	fseek(F,i,SEEK_SET);
	}

      i=0;
      do
       {
       ch=fgetc(F);

       if(ch=='{') i++;
       if(ch=='}') i--;
       if(ch=='%') readln(F);
       if(feof(F)) goto FINISH;
       } while( !((i==0) && isalnum(ch)) );
      continue;
      }

      if(!strcmp(a,"cmap") && Palette!=NULL)
	{
	ReadWord(F,a,sizeof(a),&ch);
	if(!strcmp(a,"readhexstring"))
	  {
	  for(i=0;i<Palette->GetSize1D();i++)
	    {
	    RGB.R = ReadxByte(F);
	    RGB.G = ReadxByte(F);
	    RGB.B = ReadxByte(F);
	    Palette->Set(i,&RGB);
	    }
	  }
	  continue;
	}

      if(!strcmp(a,"DisplayImage"))
	{
	if(ReadInt(F,&ch)==0) continue;		//x translation
	if(ReadInt(F,&ch)==0) continue;		//y translation
	ReadWord(F,a,sizeof(a),&ch);		//x scale
	ReadWord(F,a,sizeof(a),&ch);		//y scale
	if(ReadInt(F,&ch)==0) continue;		//pointsize
			//read label !!!
	if((x=ReadInt(F,&ch))==0) continue;	//columns

	if((y=ReadInt(F,&ch))==0) continue;	//rows
	if((Class=ReadInt(F,&ch))==0) continue; //class type

	if((i=ReadInt(F,&ch))==0) continue;	//compression
	if(i!=1) continue;			//compressed?
	if(Class==0)
	  {
	  i=planes=24;
	  goto readRAS;
	  }
	ReadWord(F,a,sizeof(a),&ch);		//?????
	if((planes=ReadInt(F,&ch))==0) continue;//planes
	goto readRAS;
	}

      x = atol(a);		//readX
      if(x<=0) continue;

      y = ReadInt(F,&ch);
      if(y<=0) continue;

      planes=ReadInt(F,&ch);
      if(planes<=0) continue;

ctiZ: ReadWord(F,a,sizeof(a));
      if(*a=='%') {readln(F);goto ctiZ;}
      if(*a!='[') continue;
      readln(F);

ctiZ2:ReadWord(F,a,sizeof(a));
      if(*a=='%') {readln(F);goto ctiZ2;}
      if(!strcmp(a,"rlecmapimage"))
	{
	RLE_Compressed=true;
	i=8;
	goto readRAS;
	}

      if(*a!='{') continue;
      readln(F);

      ch=0;
      while(ch!=10 && ch!=13)
	{
	ReadWord(F,a,sizeof(a),&ch);
	if(!strcmp(a,"colorimage")) planes*=3;
	}

readRAS:
      Raster=CreateRaster2D(x,y,planes);
      if(Raster==NULL) goto FINISH;
/*
      if(planes>2*i)
		begin
		 p.typ:='C';
		 i:=3*i;
		end;
*/

      i = (planes*Raster->GetSize1D()+7)/8 - 1;
//    if(AlineProc!=NULL) AlineProc->InitPassing(p.y,'Loading PS');

      for(y=0; y<Raster->Size2D; y++)
	{
	ptrB=(uint8_t *)Raster->GetRow(y);

	if(RLE_Compressed)
	  {
	  x=0;
	  while(x<=i)
	    {
	    RGB.R = ReadxByte(F);
	    if(RGB.R<=0x80)
	      {
	      RGB.G = ReadxByte(F);
	      while((RGB.R&0x80) == 0)
		{
		*ptrB++ = RGB.G;
		x++;
		RGB.R--;
		}
	      }
	    else
	      {
	      while(RGB.R>=0x80)
		{
		*ptrB++=ReadxByte(F);
		x++;
		RGB.R--;
		}
	      }

	   }
          }
	else for(x=0; x<=i; x++)
	   {
	   *ptrB++ = ReadxByte(F);
	   if(feof(F)) goto FINISH;
	   }
//	if AlineProc<>nil then AlineProc^.NextLine;
	}

   break;
   }

FINISH:
 fclose(F); F=NULL;
 Img.AttachRaster(Raster);
 Img.AttachPalette(Palette); 
 return(Img);
}
#endif


#if SupportEPS>=3


/** This function returns true, when arg decimal part is close to zero. */
static int IsZero2p(double arg)
{
int argi;
  argi = (int)(arg + 0.5);
  arg = fabs(arg - argi);
return arg<0.005;
}


#define WPG2PS(x) ((x)*71/25.4)

typedef struct
{
  uint32_t Key;
  const char *Blob;
} TDictionaryPS;

static const TDictionaryPS PsDict[] =
{
 {EPS_accentshow,
	"\n"
	"/accentshow {\n"
	"gsave dup\n"
	"stringwidth pop 2 div\n"
	"2 index stringwidth pop 2 div sub\n"
	"0 rmoveto\n"
	"1 index show\n"
	"grestore show pop\n"
	"} def\n"
	"\n"},
 {EPS_ACCENTSHOW,
	"\n"
	"/ACCENTSHOW {\n"
	"gsave dup\n"
	"stringwidth pop 2 div\n"
	"2 index stringwidth pop 2 div sub\n"
	"3\n"
	"rmoveto\n"
	"1 index show\n"
	"grestore show pop\n"
	"} def\n"
	"\n"},
 {EPS_rlecmapimage,
	"% rlecmapimage expects to have 'w h bits matrix' on stack\n"
	"/rlecmapimage {\n"
	"  /buffer 1 string def\n"
	"  /rgbval 3 string def\n"
	"  /block  384 string def\n"
	"\n"
	"  % proc to read a block from file, and return RGB data\n"
	"  { currentfile buffer readhexstring pop\n"
	"    /bcount exch 0 get store\n"
	"    bcount 128 ge\n"
	"    {  % it's a non-run block\n"
	"      0 1 bcount 128 sub\n"
	"      { currentfile buffer readhexstring pop pop\n"
	"\n"
	"        % look up value in color map\n"
	"	 /rgbval cmap buffer 0 get 3 mul 3 getinterval store\n"
	"\n"
	"	 % and put it in position i*3 in block\n"
	"	 block exch 3 mul rgbval putinterval\n"
	"      } for\n"
	"      block  0  bcount 127 sub 3 mul  getinterval\n"
	"    }\n"
	"\n"
	"    { % else it's a run block\n"
	"      currentfile buffer readhexstring pop pop\n"
	"\n"
	"      % look up value in colormap\n"
	"      /rgbval cmap buffer 0 get 3 mul 3 getinterval store\n"
	"\n"
	"      0 1 bcount { block exch 3 mul rgbval putinterval } for\n"
	"\n"
	"      block 0 bcount 1 add 3 mul getinterval\n"
	"    } ifelse\n"
	"  } % end of proc\n"
	"  false 3 colorimage\n"
	"} bind def\n"},
 {EPS_DrawElipse,
	"\n"
	"/DrawEllipse {\n"
	"	/endangle exch def\n"
	"	/startangle exch def\n"
	"	/yrad exch def\n"
	"	/xrad exch def\n"
	"	/y exch def\n"
	"	/x exch def\n"
	"	/savematrix mtrx currentmatrix def\n"
	"	x y translate xrad yrad scale 0 0 1 startangle endangle arc\n"
	"	savematrix setmatrix\n"
	"	} def\n"
	"\n"},
 {EPS_colorimage,
	"% define 'colorimage' if it isn't defined\n"
	"%   ('colortogray' and 'mergeprocs' come from xwd2ps\n"
	"%     via xgrab)\n"
	"/colorimage where   % do we know about 'colorimage'?\n"
	"  { pop }           % yes: pop off the 'dict' returned\n"
	"  {                 % no:  define one\n"
	"  /colortogray {  % define an RGB->I function\n"
	"    /rgbdata exch store    % call input 'rgbdata'\n"
	"    rgbdata length 3 idiv\n"
	"    /npixls exch store\n"
	"    /rgbindx 0 store\n"
	"    0 1 npixls 1 sub {\n"
	"      grays exch\n"
	"      rgbdata rgbindx       get 20 mul    % Red\n"
	"      rgbdata rgbindx 1 add get 32 mul    % Green\n"
	"      rgbdata rgbindx 2 add get 12 mul    % Blue\n"
	"      add add 64 idiv      % I = .5G + .31R + .18B\n"
	"      put\n"
	"      /rgbindx rgbindx 3 add store\n"
	"    } for\n"
	"    grays 0 npixls getinterval\n"
	"  } bind def\n"
	"\n"
	"% Utility procedure for colorimage operator.\n"
	"% This procedure takes two procedures off the\n"
	"% stack and merges them into a single procedure.\n"
	"\n"
	"/mergeprocs { % def\n"
	"  dup length\n"
	"  3 -1 roll\n"
	"  dup\n"
	"  length\n"
	"  dup\n"
	"  5 1 roll\n"
	"  3 -1 roll\n"
	"  add\n"
	"  array cvx\n"
	"  dup\n"
	"  3 -1 roll\n"
	"  0 exch\n"
	"  putinterval\n"
	"  dup\n"
	"  4 2 roll\n"
	"  putinterval\n"
	"} bind def\n"
	"/colorimage { % def\n"
	"  pop pop     % remove ""false 3"" operands\n"
	" {colortogray} mergeprocs\n"
	"image\n"
	"} bind def\n"
	"} ifelse     % end of 'false' case\n"
	"\n"},

 {EPS_FillBox1,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-4 -4 4 4]\n"
	"/XStep 8 /YStep 8\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"  0 0 moveto\n"
	"  4 0 rlineto\n"
	"  0 4 rlineto\n"
	" -4 0 rlineto\n"
	"  0 -8 rlineto\n" 
	" -4 0 rlineto\n"
	"  0 4 rlineto\n"
	" closepath\n"
	"fill\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/BoxFill1 exch def\n"
	"\n"},
 {EPS_FillBox2,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-8 -8 8 8]\n"
	"/XStep 16 /YStep 16\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"  0 0 moveto\n"
	"  8 0 rlineto\n"
	"  0 8 rlineto\n"
	" -8 0 rlineto\n"
	"  0 -16 rlineto\n" 
	" -8 0 rlineto\n"
	"  0 8 rlineto\n"
	" closepath\n"
	"fill\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/BoxFill2 exch def\n"
	"\n"},
 {EPS_FillBricks,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-4 -4 4 4]\n"
	"/XStep 8 /YStep 8\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"0.4 setlinewidth\n"
	" -4 -2 moveto 8 0 rlineto\n"
	" -4 2 moveto 8 0 rlineto\n"
	" -2 -2 moveto 0 4 rlineto\n"
	" 2 -4 moveto 0 2 rlineto\n"
	" 2 -4 moveto 0 2 rlineto\n"
	" 2 2 moveto 0 2 rlineto\n"
	"stroke\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/BrickFill exch def\n"
	"\n"},
 {EPS_FillDiagBricks,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-4 -4 4 4]\n"
	"/XStep 8 /YStep 8\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	" 0.4 setlinewidth\n"
	" -4 2 moveto 2 2 rlineto\n"
	"  2 -4 moveto 2 2 rlineto\n"
	" -4 -2 moveto 6 6 rlineto\n"
	" -2 -4 moveto 6 6 rlineto\n"
	" -3 3 moveto 2 -2 rlineto\n"
	"  3 -3 moveto -2 2 rlineto\n"
	" -3 -1 moveto 2 -2 rlineto\n"
	"  3 1 moveto -2 2 rlineto\n"
	"stroke\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/DiagBrickFill exch def\n"
	"\n"},
 {EPS_FillHoneycomb,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-3 -4 3 4]\n"
	"/XStep 6 /YStep 8\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	" 0.4 setlinewidth\n"
	" 1 2 moveto 1 -2 rlineto -1 -2 rlineto -2 0 rlineto -1 2 rlineto 1 2 rlineto closepath\n"
	" 1 2 moveto 1 2 rlineto\n"
	" -1 2 moveto -1 2 rlineto\n"
	" 1 -2 moveto 1 -2 rlineto 1 0 rlineto\n"
	" -1 -2 moveto -1 -2 rlineto -1 0 rlineto\n"
	" 2 0 moveto 1 0 rlineto\n"
	" -2 0 moveto -1 0 rlineto\n"
	"stroke\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/HoneycombFill exch def\n"
	"\n"},
 {EPS_FillPlus,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-5 -5 5 5]\n"
	"/XStep 10 /YStep 10\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"  -2 -2 moveto\n"
	"  1.5 0 rlineto 0 -1.5 rlineto -1.5 0 rlineto 0 -1.5 rlineto -1.5 0 rlineto\n"
	"  0 1.5 rlineto -1.5 0 rlineto 0 1.5 rlineto 1.5 0 rlineto 0 1.5 rlineto\n"
	"  1.5 0 rlineto\n"
	"  closepath\n"
	"  3 3 moveto\n"
	"  1.5 0 rlineto 0 -1.5 rlineto -1.5 0 rlineto 0 -1.5 rlineto -1.5 0 rlineto\n"
	"  0 1.5 rlineto -1.5 0 rlineto 0 1.5 rlineto 1.5 0 rlineto 0 1.5 rlineto\n"
	"  1.5 0 rlineto\n"
	"  closepath\n"
	"fill\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/PlusFill exch def\n"
	"\n"},
 {EPS_FillTriangle, 
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-3 -3 3 3]\n"
	"/XStep 8 /YStep 8\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	" -3 3 moveto\n"
	"  6 0 rlineto 0 -6 rlineto\n"
	" closepath\n"
	"fill\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/TriangleFill exch def\n"
	"\n"},
 {EPS_FillSmSquare,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-2 -2 2 2]\n"
	"/XStep 5 /YStep 5\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	" -2 2 moveto\n"
	"  4 0 rlineto 0 -4 rlineto -4 0 rlineto\n"
	" closepath\n"
	"stroke\n"	
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/SmSquareFill exch def\n"
	"\n"},
 {EPS_FillBalls,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-5 -5 5 5]\n"
	"/XStep 10 /YStep 10\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"0.4 setlinewidth\n"
	"-2.5 -2.5 moveto\n"
	"-2.5 -2.5 2.5 160 430 arc\n"	// ARC draws a line from a current point
	"fill\n"
	"-2.5 -2.5 2.5 70 160 arc\n"
	"stroke\n"
	"2.5 2.5 moveto\n"
	"2.5 2.5 2.5 160 430 arc\n"
	"fill\n"
	"2.5 2.5 2.5 70 160 arc\n"
	"stroke\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/BallsFill exch def\n"
	"\n"},
 {EPS_FillFishscale,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-3 -3 3 3]\n"
	"/XStep 6 /YStep 6\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"0.4 setlinewidth\n"
	"0 3 3 180 360 arc\n"
	"-3 -3 moveto -3 0 2.9 270 360 arc\n"
	"0 0 moveto 3 0 2.9 180 270 arc\n"
	"stroke\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/FishscaleFill exch def\n"
	"\n"},
 {EPS_FillArch,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-3 -3 3 3]\n"
	"/XStep 6 /YStep 6\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"0.4 setlinewidth\n"
	"-3 3 moveto 3 0 3 90 180 arc -3 0 3 0 90 arc closepath\n"
	"fill\n"
	"-3 0 moveto 3 0 lineto 0 -3 3 0 180 arc closepath\n"
	"fill\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/ArchFill exch def\n"
	"\n"},
 {EPS_FillChainlink,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-2 -3 2 3]\n"
	"/XStep 4 /YStep 6\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	" 0.4 setlinewidth\n"
	" 1 1 moveto -1 1 rlineto -1 -1 rlineto 0 -2 rlineto 1 -1 rlineto 1 1 rlineto closepath\n"
	" 1 1 moveto 1 1 rlineto\n"
	" 1 -1 moveto 1 -1 rlineto\n"
	" -1 1 moveto -1 1 rlineto 0 1 rlineto\n"
	" -1 -1 moveto -1 -1 rlineto 0 -1 rlineto\n"
	" 0 2 moveto  0 1 rlineto\n"
	" 0 -2 moveto  0 -1 rlineto\n"
	"stroke\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/ChainlinkFill exch def\n"
	"\n"},
 {EPS_FillPatio,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-3 -3 3 3]\n"
	"/XStep 6 /YStep 6\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"0.4 setlinewidth\n"
	"0 -1 1 0 180 arc\n"
	"1 -1 moveto 1 0 1 270 450 arc\n"
	"0 2 1 0 180 arc\n"
	"-1 0 1 90 270 arc\n"
	"-1 -1 moveto 0 -1 rlineto\n"
	"1 -1 moveto 0 -1 rlineto\n"
	"2 -3 moveto 1 -3 1 0 90 arc\n"
	"-1 -2 moveto -1 -3 1 90 180 arc\n"
	"1 2 moveto 1 3 1 270 360 arc\n"
	"-2 3 moveto -1 3 1 180 270 arc\n"
	"3 1 moveto 3 0 1 90 180 arc\n"
	"3 -2 moveto 3 -3 1 90 180 arc\n"
	"-2 0 moveto -3 0 1 0 90 arc\n"
	"-2 -3 moveto -3 -3 1 0 90 arc\n"
	"stroke\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/PatioFill exch def\n"
	"\n"},
 {EPS_FillWeave,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-4 -4 4 4]\n"
	"/XStep 8 /YStep 8\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"0.4 setlinewidth\n"
	"-2 0 moveto -2 2 rlineto\n"
	"0 2 moveto 2 2 rlineto\n"
	"2 0 moveto 2 -2 rlineto\n"
	"0 -2 moveto -2 -2 rlineto\n"
	"stroke\n"
	"-2 0 moveto 2 2 rlineto 2 -2 rlineto -2 -2 rlineto closepath\n"
	"fill\n"
	"-4 -4 moveto 0 2 rlineto 2 -2 rlineto closepath fill\n"
	"-4 4 moveto 2 0 rlineto -2 -2 rlineto closepath fill\n"
	"4 4 moveto 0 -2 rlineto -2 2 rlineto closepath fill\n"
	"4 -4 moveto -2 0 rlineto 2 2 rlineto closepath fill\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/WeaveFill exch def\n"
	"\n"},
 {EPS_FillWaves,
	"\n"
	"<<		% Begin prototype pattern dictionary\n"
	"/PaintType 2    % Uncolored pattern\n"
	"/PatternType 1\n"
	"/TilingType 1\n"
	"/BBox [-4 -4 4 4]\n"
	"/XStep 8 /YStep 8\n"
	"/PaintProc\n"
	"{ pop		% Pop pattern dictionary\n"
	"0.4 setlinewidth\n"
	"-4 1 moveto -2 2 lineto\n"
	"0 2 moveto -1 2 1 0 180 arc\n"
	"0 2 moveto 0 1 1 90 270 arc\n"
	"2 0 lineto\n"
	"4 1 lineto\n"
	"4 -2 moveto 3 -2 1 0 180 arc\n"
	"-2 -3.8 lineto\n"
	"-4 -3.8 lineto\n"
	"4 -1 moveto 4 -3 1 90 270 arc\n"
	"stroke\n"
	"}\n"
	">>		% End prototype pattern dictionary\n"
	"matrix		% Identity matrix\n"
	"makepattern	% Instantiate the pattern\n"
	"/WavesFill exch def\n"
	"\n"}
};


int SavePictureEPS(const char *Name, const Image &Img)
{
FILE *f;
const Image *CurrImg;
FloatBBox bbx;			//float x1,y1,x2,y2;
uint32_t MakeDict = 0;

  if(Img.isEmpty()) return(ErrEmptyRaster);

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

  CurrImg = &Img;		//computation of bounding box
  bbx.MaxX = bbx.MinX = Img.x + Img.dx/2.0;
  bbx.MaxY = bbx.MinY = Img.y + Img.dy/2.0;
  while(CurrImg!=NULL)
    {
    UpdateBBox(bbx, CurrImg->RotAngle,
	       CurrImg->x,CurrImg->y,CurrImg->dx,CurrImg->dy);

    if(CurrImg->Raster==NULL && CurrImg->VecImage!=NULL)
      {
      CurrImg->VecImage->FeaturesEPS(MakeDict);
      }

    if(CurrImg->Palette!=NULL)
      {
      switch(CurrImg->ImageType())
        {
	case ImageNone:      break;
        case ImageGray:	     break;
        case ImageTrueColor: MakeDict |= EPS_colorimage;  //fake true color - should be fixed
			     break;
        case ImagePalette:   MakeDict |= EPS_rlecmapimage;
			     break;
         }
      }

    if(CurrImg->Raster)
	if(CurrImg->Raster->GetPlanes()==24 || CurrImg->Raster->Channels()>=3)
		MakeDict |= EPS_colorimage; //RGB true color image

    CurrImg = CurrImg->Next;
    }

  if((fabs(bbx.MaxX)<=1e-6 || fabs(bbx.MaxY)<=1e-6) &&
      (fabs(bbx.MaxX-bbx.MinX)<=1e-5 || fabs(bbx.MaxY-bbx.MinY)<=1e-5))
    {   	//If size information is not present, calculate it.
    if(Img.Raster!=NULL)
      {
      bbx.MaxX = Img.Raster->Size1D;
      bbx.MaxY = Img.Raster->Size2D;
      }
    if(bbx.MaxY<=1e-6) bbx.MaxY=1;

    double ratio = bbx.MaxX/bbx.MaxY;
    if(ratio >= 612.0/792.0)
      {		//x should be shortened
      ratio = 612.0*25.4/(71.0*bbx.MaxX);	//calculate in mm
      }
    else
      {		//y should be shortened
      ratio = 792.0*25.4/(71.0*bbx.MaxY);	//calculate in mm
      }
    bbx.MaxX *= ratio;
    bbx.MaxY *= ratio;
    bbx.MinX = bbx.MinY = 0;
    }

  CurrImg = &Img;

  fprintf(f,
      "%%!PS-Adobe-2.0 EPSF-2.0\n"
      "%%%%Title: %s\n"					//%title
      "%%%%Creator: %s - (c) 2000-2025 by Jaroslav Fojtik\n"	//%creator
      "%%%%BoundingBox: ",
          Name, UnitName);

  fprintf(f,"%2.0f %2.0f %2.0f %2.0f",
      floor(WPG2PS(bbx.MinX)), floor(WPG2PS(bbx.MinY)), ceil(WPG2PS(bbx.MaxX)), ceil(WPG2PS(bbx.MaxY)));
  if(!(IsZero2p(WPG2PS(bbx.MinX)) && IsZero2p(WPG2PS(bbx.MinY)) && IsZero2p(WPG2PS(bbx.MaxX)) && IsZero2p(WPG2PS(bbx.MaxY))))
  {
    fprintf(f,"\n%%%%HiResBoundingBox: %2.2f %2.2f %2.2f %2.2f",
      WPG2PS(bbx.MinX), WPG2PS(bbx.MinY), WPG2PS(bbx.MaxX), WPG2PS(bbx.MaxY));
  }

  fputs("\n"
      "%%Pages: 1\n"
      "%%DocumentFonts:\n"
      "%%EndComments\n"
      "%%EndProlog\n"
      "\n"
      "%%Page: 1 1", f);

  if(MakeDict)
	{
	fputs("\n"
	      "/$F2psDict 200 dict def\n"
	      "$F2psDict begin\n"
	      "$F2psDict /mtrx matrix put\n"
	      "\n", f);

	for(unsigned i=0; i<sizeof(PsDict)/sizeof(TDictionaryPS); i++)
	  if(MakeDict & PsDict[i].Key) fputs(PsDict[i].Blob,f);
	}

	/** Store all rasters to image here. */
  while(CurrImg!=NULL)
    {
    fputs("\n"
          "% remember original state\n"
          "/origstate save def\n"
          "\n"
          "% build a temporary dictionary\n"
          "20 dict begin\n", f);
   if(CurrImg->VecImage!=NULL)
	{
        fputs("% ",f);       
        CurrImg->VecImage->Export2EPS(f);
        fputs("\n",f);
	}
  
   if(fabs(CurrImg->dx)<1e-6 || fabs(CurrImg->dy)<1e-6)     
       DumpRaster2File(f, CurrImg->Raster, CurrImg->Palette,
		WPG2PS(bbx.MaxX+bbx.MinX)/2.0f,		// Sx
	        WPG2PS(bbx.MaxY+bbx.MinY)/2.0f,		// Sy
		WPG2PS(bbx.MaxX-bbx.MinX)/2.0f,		// dx
		WPG2PS(bbx.MaxY-bbx.MinY)/2.0f,		// dy
		CurrImg->RotAngle, CurrImg->ImageType());
   else
       DumpRaster2File(f, CurrImg->Raster, CurrImg->Palette,
		WPG2PS((CurrImg->x + fabs(CurrImg->dx)/2)),
		WPG2PS((CurrImg->y + fabs(CurrImg->dy)/2)),
		WPG2PS(CurrImg->dx/2), WPG2PS(CurrImg->dy/2), CurrImg->RotAngle, CurrImg->ImageType());

    fputs("% stop using temporary dictionary\n"
      "end\n"
      "\n"
      "% restore original state\n"
      "origstate restore\n", f);

    CurrImg=CurrImg->Next;
    }

  if(MakeDict) fputs("\nend  %finish global function dictionary", f);

  fputs("\n"
      "showpage\n"
      "\n"
      "%%Trailer", f);
  
  fclose(f);
  return(0);
}
#endif

#endif
//-------------------End of EPS routines------------------


//-------------------------FITS---------------------------
#ifdef SupportFITS


static void FixSignedMSBValues(unsigned char *data, int size, unsigned step)
{
  while(size-->0)
  {
    *data ^= 0x80;
    data += step;
  }
}

static void FixSignedLSBValues(unsigned char *data, int size, unsigned step)
{
  data += step - 1;
  while(size-->0)
  {
    *data ^= 0x80;
    data += step;
  }
}

#if SupportFITS>=4 || SupportFITS==2
/** States of FITS parser. */
typedef enum
{
  HeaderStart,
  HeaderContinue,
  HeaderEnd,
  ExtensionStart,
  ExtensionContinue,
  ExtensionEnd,
  ExtensionData,
  ExtensionBinData
} FITS_PARSE;

#define MAX_AXIS 4

/** This procedure process one separate fits file. */
Image LoadPictureFITS(const char *Name)
{
char HDU_LINE[80];
char identifier[80];
char value[80];
char comment[80];
char c;
char BitPix = 0;
uint8_t pos;
FILE *F;
unsigned j;
char Simple = 0;
bool ExtensionExists = false;
int Naxis;
int Axes[MAX_AXIS];
FITS_PARSE ParseStatus;
int Endian=1;			///< Default BIG endiann
Image Img;
Raster2DAbstract *Raster=NULL;
uint32_t ldblk;

  if((F=fopen(Name,"rb"))==NULL) return(NULL);
  //LogMessage(LOG_INFO,_("Parsing file: %s"),FileName).Print();

  ParseStatus = HeaderStart;

NewChunk:
  memset(Axes,0,sizeof(Axes));
  // memset(&attr,0,sizeof(attr));
  Naxis = 0;
  memset(HDU_LINE,0,sizeof(HDU_LINE));

  while(!feof(F))
  {
    for(int i=0; i<36; i++)        // Parse separate rows in HDU.
    {
      if(fread(HDU_LINE,sizeof(HDU_LINE),1,F) != 1) goto ExitPoint;

      *identifier = *value = *comment = 0;
	  // parse identifier
      pos=0; j=0;
      while(j<sizeof(identifier)-1)
	{
	c = HDU_LINE[j++];
	if(isspace((unsigned char)c)) continue;
	if(c=='=') break;
	if(c=='/') goto COMMENT;

	identifier[pos++] = c;
	identifier[pos] = 0;
	}

	  // parse value
      pos = 0;
      while(j<80)
        {
	c = HDU_LINE[j++];
        if(isspace((unsigned char)c) && *value==0) continue;
	if(c=='/') break;
	value[pos++] = c;
        value[pos] = 0;
        }
      if(pos>0) pos--;
      while(pos>0)        // remove leading spaces
        {
	if(!isspace((unsigned char)value[pos])) break;
        value[pos--] = 0;
        }

COMMENT:
      pos = 0;
      while(j<80)
	{
	c = HDU_LINE[j++];
        if(isspace((unsigned char)c) && *comment==0) continue;
        comment[pos++] = c;
	comment[pos] = 0;
        }
      if(pos>0) pos--;
      while(pos>0)        // remove leading spaces
	{
	if(!isspace((unsigned char)comment[pos])) break;
	comment[pos--] = 0;
	}

      if(*identifier==0 && *value==0 && *comment==0)
	continue;         // line is empty

	// parse separate keywords
      if(!strcmp(identifier,"BITPIX"))
	{
	BitPix = atoi(value);
	}

      if(!strcmp(identifier,"END"))
	{
	switch(ParseStatus)
	  {
	  case HeaderStart:
	  case HeaderContinue:   ParseStatus=HeaderEnd;
	  case HeaderEnd:        break;
	  case ExtensionStart:
	  case ExtensionContinue:ParseStatus=ExtensionEnd;
	  case ExtensionEnd:     break;
	  }
	}

      if(!strcmp(identifier,"EXTEND"))
	{
	if(*value=='T') ExtensionExists = true;
	if(*value=='F') ExtensionExists = false;
	}

      if(!strncmp(identifier,"NAXIS",5))
	{
	if(identifier[5]==0)
	  Naxis = atoi(value);
	else
	  {
	  int AxNumber;

	  AxNumber = atoi(identifier+5) - 1;
	  if(AxNumber<MAX_AXIS && AxNumber>=0)
	    {
	    Axes[AxNumber] = atoi(value);
	    }
	  }
	}

      if(!strcmp(identifier,"SIMPLE"))
	Simple = *value;

      if(!strcmp(identifier,"XENDIAN"))
	{
	if(!strcmp(value,"'BIG'") || !strcmp(value,"BIG"))
	  Endian = 1;
	else
	  Endian = 0;
	}

      //printf("%s = %s / %s\n", identifier, value, comment);
      }

//     {LogMessage(LOG_DEBUG,_("ParseStatus %d of file %s"),ParseStatus,FileName).Print();}

     switch(ParseStatus)
       {
       case HeaderStart:      if(Simple!='T') goto ExitPoint;
			      if(BitPix==0) goto ExitPoint;
			      ParseStatus=HeaderContinue; break;
       case HeaderContinue:   break;
       case ExtensionStart:   ParseStatus=ExtensionContinue; break;
       case ExtensionContinue:break;
       case HeaderEnd:        // continues, no break please
       case ExtensionEnd:
	      {        // data should be read just here
	      if(Naxis>0)
		{
		long len = (Axes[0]==0) ? 1 : Axes[0];

		for(int i=1;i<Naxis;i++)
		  if(Axes[i]>0)
		    len *= Axes[i];

		len = labs(len * BitPix) / 8;  //fix for a proper length in bytes

		//printf(" len = %d, Bitpix = %d, Naxis = %d ", len, BitPix, Naxis);

		long FilePos = ftell(F);

		if(Naxis==2)
		{

		  Raster = CreateRaster2D(Axes[0],Axes[1],BitPix);
		  if(Raster==NULL) goto ExitPoint;

		  ldblk = (labs(Raster->GetPlanes())*Raster->GetSize1D()+7)/8;
		  j = Raster->Size2D;
		  while(j-- > 0)
		  {
		    uint8_t *RowData = (uint8_t*)Raster->GetRow(j);
		    if(fread(RowData,ldblk,1,F)!=1) {goto ExitPoint;}
#ifdef HI_ENDIAN
		    if(Endian == 0)
#else
		    if(Endian == 1)
#endif
		    {
		      switch(BitPix)
		      {
			case  64: FixSignedMSBValues(RowData, Raster->GetSize1D(), 8);
			case -64: swab64(RowData,Raster->GetSize1D());
				  break;
			case  32: FixSignedMSBValues(RowData, Raster->GetSize1D(), 4);
			case -32: swab32(RowData,Raster->GetSize1D());
				  break;
			case  16: swab16(RowData,Raster->GetSize1D());
				  break;
		      }
		    }
                    else
                    {
                      if(BitPix==64)
			FixSignedLSBValues(RowData, Raster->GetSize1D(), 8);
		      if(BitPix==32)
			FixSignedLSBValues(RowData, Raster->GetSize1D(), 4);
                    }

		//	AlineProc(j,p);
		  }
		} else if(Naxis==3)
		{
		  if(Axes[0]==3 && (BitPix==8 || BitPix==16))	// This is considered as RGB.
		  {
		    Raster = CreateRaster2DRGB(Axes[1],Axes[2],BitPix);
		    if(Raster==NULL) goto ExitPoint;

		    ldblk = (labs(Raster->GetPlanes())*Raster->GetSize1D()+7)/8;
		    for(j=0; j<Raster->Size2D; j++)
		    {
		      if(fread(Raster->GetRow(Raster->Size2D-j-1),ldblk,1,F)!=1) {goto ExitPoint;}
		    }
		  }
		}

		Img.AttachRaster(Raster);

		len = (len+2879)/2880;  //amount of blocks
		fseek(F, FilePos+len*2880, SEEK_SET);

//	        LogMessage(LOG_DEBUG,_("Processed %d blocks"),len).Print();
		}

	      if(ExtensionExists)
		 {
		 ParseStatus=ExtensionStart;
		 goto NewChunk;
		 }
	      }
       }

    } // header loop

ExitPoint:
  if(F) {fclose(F);F=NULL;}

  if(Raster!=NULL && Raster->UsageCount==0) delete Raster;
  return(Img);
}
#endif


#if SupportFITS>=3

/** This functions inserts one row into a HDU. */
static int InsertRowHDU(char *buffer, const char *data)
{
size_t len;

  if(data==NULL) return 0;
  len = strlen(data);

  if(len>80) len = 80;

  (void)strncpy(buffer,data,len);
  return len;
}


int SavePictureFITS(const char *Name, const Image &Img)
{
FILE *f;
char HDU[36][80];
char depth;
char channels;
Raster1DAbstract *RPixels = NULL;
unsigned ldblk;
unsigned y;
void (*swabXX)(unsigned char *block, int PixelCount);

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

	/* Open output image file. */
 if((f=fopen(Name,"wb"))==NULL) return(ErrOpenFile);

 channels = 1;
 depth = Img.Raster->GetPlanes();

 if(depth > 8)
   {
   if(depth > 16)
     {
     if(Img.Raster->Channels()==3 && depth==24)
     {
       depth = 8;
       channels = 3;
     }
     else if(depth > 32)
       depth=64;
     else
       depth=32;
     }
   else
     depth=16;
   }
 else
   depth=8;

  ldblk = Img.Raster->GetSize1D() * ((depth*channels)/8);
  switch(Img.Raster->GetPlanes())
  {
    case -64:ldblk = Img.Raster->GetSize1D()*8; depth=-64; break;
    case -32:ldblk = Img.Raster->GetSize1D()*4; depth=-32; break;
    case 8:  break;
    case 16: break;
    case 32: break;
    case 64: break;	// no pixels intermediate buffer needed
    case 24: if(channels==3) break;
		// fallback, use conversion for 24bit gray
    default: RPixels = CreateRaster1D(Img.Raster->GetSize1D(), depth);
	     if(RPixels->Data1D==NULL) goto FINISH;
  }

	/* Initialize image header. */
  memset(HDU,' ',sizeof(HDU));
  InsertRowHDU(HDU[0], "SIMPLE  =                    T");
  y = sprintf(HDU[1],  "BITPIX  =                    %d", (int)depth);
  HDU[1][y] = ' ';
  if(channels>1)
  {
    InsertRowHDU(HDU[2], "NAXIS   =                  3");
    y = sprintf(HDU[3],  "NAXIS1  =           %u", (unsigned)channels);
    HDU[3][y] = ' ';
    y = sprintf(HDU[4],  "NAXIS2  =           %10lu",(unsigned long)Img.Raster->GetSize1D());
    HDU[4][y] = ' ';
    y = sprintf(HDU[5],  "NAXIS3  =           %10lu",(unsigned long)Img.Raster->Size2D);
    HDU[5][y] = ' ';
  }
  else
  {
    InsertRowHDU(HDU[2], "NAXIS   =                  2");
    y = sprintf(HDU[3],  "NAXIS1  =           %10lu",(unsigned long)Img.Raster->GetSize1D());
    HDU[3][y] = ' ';
    y = sprintf(HDU[4],  "NAXIS2  =           %10lu",(unsigned long)Img.Raster->Size2D);
    HDU[4][y] = ' ';
  }

  {
  int row = 5;
  if(channels>1) row=6;

  if(Img.Raster->GetPlanes()<0)
    {
    double val;
    double Min,Max;

    Min = Max = Img.Raster->GetValue2Dd(0,0);
    for(y=0;y<Img.Raster->Size2D;y++)
      for(unsigned x=0;x<Img.Raster->GetSize1D();x++)
	{
	val = Img.Raster->GetValue2Dd(x,y);
	if(val<Min) Min=val;
	if(val>Max) Max=val;
	}
    y = sprintf(HDU[row],  "DATAMIN =           %10f",Min);
    HDU[row++][y] = ' ';
    y = sprintf(HDU[row],  "DATAMAX =           %10f",Max);
    HDU[row++][y] = ' ';
    }
  else
    {
    y = sprintf(HDU[row],  "DATAMIN =           %10u",0);
    HDU[row++][y] = ' ';
    if(depth==64)
      y = sprintf(HDU[row],  "DATAMAX =  9223372036854775807");	// "DATAMAX =  %19llu",0x7FFFFFFFFFFFFFFFll);
    else
      y = sprintf(HDU[row],  "DATAMAX =           %10lu",(unsigned long)1<<(Img.Raster->GetPlanes()/channels));

    HDU[row++][y] = ' ';
    }
  if(depth>8 && depth<64)
    {
    y = sprintf(HDU[row],"BZERO   =           %10u", (depth<=16) ? 32768U : 2147483648U);
    HDU[row++][y] = ' ';
    }
  InsertRowHDU(HDU[row++], "HISTORY Created by pictures.");
  InsertRowHDU(HDU[row],   "END");
  }
  fwrite((void *)HDU,1,sizeof(HDU),f);

#ifdef HI_ENDIAN
  swabXX = NULL;
#else
  switch(depth)
  {
    case -64:
    case  64:swabXX=swab64; break;
    case -32:
    case  32:swabXX=swab32; break;
    case  16:swabXX=swab16; break;
    default: swabXX=NULL; break;
  }
#endif

	/* Convert image to fits scale PseudoColor class. */
  y = Img.Raster->Size2D;
  while(y-->0)
    {
    if(RPixels)
      {
      RPixels->Set(*Img.Raster->GetRowRaster(y));
      fwrite(RPixels->Data1D,ldblk,1,f);
      }
    else
      {
      uint8_t *RowData = (uint8_t *)Img.Raster->GetRow(y);
      if(swabXX!=NULL) swabXX(RowData, Img.Raster->GetSize1D());
      if(depth==64)
	FixSignedMSBValues(RowData, Img.Raster->GetSize1D(), 8);
      if(depth==32)
	FixSignedMSBValues(RowData, Img.Raster->GetSize1D(), 4);

      fwrite(Img.Raster->GetRow(y),ldblk,1,f);

      if(depth==64)
	FixSignedMSBValues(RowData, Img.Raster->GetSize1D(), 8);
      if(depth==32)
	FixSignedMSBValues(RowData, Img.Raster->GetSize1D(), 4);
       if(swabXX!=NULL) swabXX(RowData, Img.Raster->GetSize1D());
      }
  }

	/* Calculate of padding */
  y = 2880 - (Img.Raster->Size2D * (long)ldblk) % 2880;
  if(y>0)
  {
    memset(HDU, 0, y);
    fwrite(HDU,y,1,f);
  }

FINISH:
  if(RPixels) {delete RPixels; RPixels=NULL;}
  fclose(f);
  return(0);
}	//SaveFITS
#endif

#endif
//------------------End of FITS routines------------------




//------------------HRZ--------------------------
#ifdef SupportHRZ

#if SupportHRZ>=4 || SupportHRZ==2
Image LoadPictureHRZ(const char *Name)
{
FILE  *f;
uint16_t y,x;
uint8_t *pByte;
long ldblk;
Raster2DAbstract *Raster=NULL;
Image Img;

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

 if(FileSize(f)!=184320)
	{
	goto ENDPROC;
	}
 Raster=CreateRaster2D(256,240,24);
 if(Raster==NULL) goto ENDPROC;
// if AlineProc<>nil then AlineProc^.InitPassing(p.y,'Loading HRZ');

 ldblk = 3*Raster->GetSize1D();
 for(y=0;y<Raster->Size2D;y++)
   {
   pByte = (uint8_t *)Raster->GetRow(y);
   if(fread(pByte,ldblk,1,f)!=1) {goto ENDPROC;}
   for(x=0;x<ldblk;x++)
      *pByte++ <<= 2;
 //	AlineProc(i,p);
   }

ENDPROC:
 fclose(f);
 Img.AttachRaster(Raster); 
 return(Img);
}

#endif


#if SupportHRZ>=3

/*
int SavePictureHRZ(const char *Name,const Image &Img)
{
FILE *f;
uint16_t ldblk;
uint16_t i,dummy;
char k;
//    Formater:PFormStruct;
char *TempData=NULL;

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

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

  if(Img.Raster->GetPlanes()!=24)
    {
    TempData = (char *)malloc(Raster->GetSize1D()*3);
    }

  for(i=0;i<Img.Raster->Size2D;i++)
	{
	if(TempData)
	  {
	  Img.Raster->GetRowRaster(i)->Get1Bit(TempData);
	  fwrite(TempData,ldblk,1,f);
	  }
	else
	  fwrite(Img.Raster->GetRow(i),ldblk,1,f);
	fwrite(&dummy,k,1,f);			//write till end of line
	// if AlineProc<>nil then AlineProc^.NextLine;
	}

FINISH:
  if(TempData) free(TempData);
  fclose(f);
  return(0);
}
*/
#endif

#endif
//-------------------End of HRZ routines------------------


//----------------------- Load MAC ----------------------
#ifdef SupportMAC

Image LoadPictureMAC(const char *Name)
{
FILE  *f;
uint16_t x8,y;
uint8_t rep,b;
uint8_t *DataPtr;
uint16_t ldblk;
Raster2DAbstract *Raster = NULL;
Image Img;

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

 RdWORD_LoEnd(&ldblk,f);
 if((ldblk & 0xFF)!=0)
	{
//	LoadPictureMAC=ErrAnother;
	goto FINISH;
	}

 if(ldblk==0)		//???? don't know why
   fseek(f,0x200,SEEK_SET);
 else
   fseek(f,0x280,SEEK_SET);

 Raster = CreateRaster2D(576,720,1);
 if(Raster==NULL) goto FINISH;
// if AlineProc!=nil then
//	AlineProc^.InitPassing(p.y,'Loading MAC');

 ldblk = (Raster->GetPlanes()*Raster->GetSize1D()) /8;

 x8=0; y=0;
 DataPtr = (uint8_t *)Raster->GetRow(0);
 while(y<Raster->Size2D)
   {
   rep = fgetc(f);
   if(feof(f)) break;

   if( rep>=128 || rep<=0)
     {
     b = ~fgetc(f);

     rep = ~rep + 2;
     while(rep>0)
       {
       *DataPtr++ = b;
       x8++;
       rep--;
       if(x8>=ldblk)
	 {
	 x8=0;
	 y++;
	 if(y>=Raster->Size2D)
	   {
           delete Raster;
           Raster = NULL;
           goto FINISH;
           }
	 DataPtr = (uint8_t *)Raster->GetRow(y);
	   //if AlineProc!=nil then AlineProc^.NextLine;
	 }
       }
      }
   else
     {
     rep++;
     while(rep>0)
       {
       b = ~fgetc(f);
       *DataPtr++ = b;
       x8++;
       rep--;
       if(x8>=ldblk)
	 {
	 x8=0;
	 y++;
	 if(y>=Raster->Size2D)
	   {
           delete Raster;
           Raster = NULL;
           goto FINISH;
           }
	 DataPtr = (uint8_t *)Raster->GetRow(y);
	   //if AlineProc!=nil then AlineProc^.NextLine;	 
	 }
       }
     }
   }

FINISH:
 fclose(f);
 //if IOResult!=0 then LoadPictureMACErrRdDisk;
 Img.AttachRaster(Raster); 
 return Img;
}  //LoadMAC

#endif
//------------------End of - Load MAC --------------------


//--------------------------MAT---------------------------
#ifdef SupportMAT


typedef struct {
	char identific[124];
	uint16_t ff_version;
	char idx[2];
	uint32_t DataType;
	uint32_t ObjectSize;
	uint32_t unknown1;
	uint32_t unknown2;

	uint32_t StructureFlagClass;
	uint32_t unknown3;
	uint32_t unknown4;
	uint32_t DimFlag;

	uint32_t SizeX;
	uint32_t SizeY;
	//Size Z optionally
	uint32_t Unknown5;		//only for 3D matrix (RGB)
	uint16_t Flag1;
	uint16_t NameFlag;
	} MATHeader;

typedef enum
  {
    miINT8 = 1,			/* 8 bit signed */
    miUINT8,			/* 8 bit unsigned */
    miINT16,			/* 16 bit signed */
    miUINT16,			/* 16 bit unsigned */
    miINT32,			/* 32 bit signed */
    miUINT32,			/* 32 bit unsigned */
    miSINGLE,			/* IEEE 754 single precision float */
    miRESERVE1,
    miDOUBLE,			/* IEEE 754 double precision float */
    miRESERVE2,
    miRESERVE3,
    miINT64,			/* 64 bit signed */
    miUINT64,			/* 64 bit unsigned */
    miMATRIX,		        /* MATLAB array */
    miCOMPRESSED,	        /* Compressed Data */
    miUTF8,		        /* Unicode UTF-8 Encoded Character Data */
    miUTF16,		        /* Unicode UTF-16 Encoded Character Data */
    miUTF32			/* Unicode UTF-32 Encoded Character Data */
  } mat5_data_type;

typedef enum
  {
    mxCELL_CLASS = 1,		/* cell array */
    mxSTRUCT_CLASS,		/* structure */
    mxOBJECT_CLASS,		/* object */
    mxCHAR_CLASS,		/* character array */
    mxSPARSE_CLASS,		/* sparse array */
    mxDOUBLE_CLASS,		/* double precision array */
    mxSINGLE_CLASS,		/* single precision floating point */
    mxINT8_CLASS,		/* 8 bit signed integer */
    mxUINT8_CLASS,		/* 8 bit unsigned integer */
    mxINT16_CLASS,		/* 16 bit signed integer */
    mxUINT16_CLASS,		/* 16 bit unsigned integer */
    mxINT32_CLASS,		/* 32 bit signed integer */
    mxUINT32_CLASS,		/* 32 bit unsigned integer */
    mxINT64_CLASS,		/* 64 bit signed integer */
    mxUINT64_CLASS,		/* 64 bit unsigned integer */
    mxFUNCTION_CLASS            /* Function handle */
  } arrayclasstype;

#define FLAG_COMPLEX 0x800
#define FLAG_GLOBAL  0x400
#define FLAG_LOGICAL 0x200

#define STRUCTURE_CLASS(x) ((x.StructureFlagClass)&0xFF)
#define STRUCTURE_FLAG(x) ((x.StructureFlagClass>>8)&0xFF)

/**This procedure loads 2D data from MATLAB MAT file*/
#if SupportMAT>=4 || SupportMAT==2

Image LoadPictureMAT(const char *Name)
{
int8_t Depth;
uint16_t iy, ix;
MATHeader MATLAB_HDR;
uint32_t size;
Image Img;
Raster2DAbstract *Raster=NULL;
FILE *f;
Raster1D_8Bit RBit8;
void (*swabXX)(unsigned char *block, int PixelCount);

uint32_t CellType,ldblk,z;
int (*RdWORD_xxEnd)( uint16_t *num, FILE *f );
int (*RdDWORD_xxEnd)( uint32_t *num, FILE *f );

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

  fread(&MATLAB_HDR,1,124+2+2,f);
  if (strncmp(MATLAB_HDR.identific,"MATLAB",6)) goto FINISH;
  if (!strncmp(MATLAB_HDR.idx,"IM",2))
    {
    RdWORD_xxEnd = RdWORD_LoEnd;
    RdDWORD_xxEnd = RdDWORD_LoEnd;
    }
  else if(!strncmp(MATLAB_HDR.idx,"MI",2))
    {
    RdWORD_xxEnd = RdWORD_HiEnd;
    RdDWORD_xxEnd = RdDWORD_HiEnd;
    }
  else goto FINISH;	//unsupported endian

  loadstruct(f,(MATLAB_HDR.idx[0]=='M')?"DDDDDDDDDD":"dddddddddd",
	 &MATLAB_HDR.DataType,	&MATLAB_HDR.ObjectSize,
	 &MATLAB_HDR.unknown1,	&MATLAB_HDR.unknown2,
	 &MATLAB_HDR.StructureFlagClass,&MATLAB_HDR.unknown3,
	 &MATLAB_HDR.unknown4,  &MATLAB_HDR.DimFlag,
	 &MATLAB_HDR.SizeX,	&MATLAB_HDR.SizeY);

  if(MATLAB_HDR.DataType!=miMATRIX) goto FINISH;
  switch(MATLAB_HDR.DimFlag)
    {
    case  8: z=1; break;	  //2D matrix
    case 12: RdDWORD_xxEnd(&z,f); //3D matrix RGB
	     RdDWORD_xxEnd(&MATLAB_HDR.Unknown5,f);
	     if(z!=3) goto FINISH;
	     break;
    default: goto FINISH;
    }
  RdWORD_xxEnd(&MATLAB_HDR.Flag1,f);
  RdWORD_xxEnd(&MATLAB_HDR.NameFlag,f);

  if(STRUCTURE_CLASS(MATLAB_HDR) != mxDOUBLE_CLASS &&
     STRUCTURE_CLASS(MATLAB_HDR) != mxSINGLE_CLASS &&
     STRUCTURE_CLASS(MATLAB_HDR) != mxUINT8_CLASS &&	//uint8 + 3D
     STRUCTURE_CLASS(MATLAB_HDR) != mxUINT16_CLASS &&	//uint16 + 3D
     STRUCTURE_CLASS(MATLAB_HDR) != mxUINT32_CLASS	//uint16 + 3D
     ) goto FINISH;

  switch(MATLAB_HDR.NameFlag)
    {
    case 0:RdDWORD_xxEnd(&size,f);	/*Object name string size*/
	   size=4*(long)((size+3+1)/4);
	   fseek(f,size,SEEK_CUR);
    case 1:
    case 2:
    case 3:
    case 4:fread(&size,1,4,f);		/*Object name string*/
	   break;
    default:goto FINISH;
    }

   RdDWORD_xxEnd(&CellType,f);	/*Additional object type*/
/*fprintf(stdout,"Cell type:%ld\n",CellType);*/

   RdDWORD_xxEnd(&size,f);	/*data size*/
//  if(size!=2) goto FINISH;

   Depth = 0;
   swabXX = NULL;
   switch(CellType)
      {
      case miUINT8:
	     if(MATLAB_HDR.StructureFlagClass & FLAG_LOGICAL)
		Depth=1;		   /*Byte type cell*/
	     else
		Depth=8;		   /*Byte type cell*/
	     ldblk=(long) MATLAB_HDR.SizeX;
	     if(MATLAB_HDR.StructureFlagClass & FLAG_COMPLEX) goto FINISH; //MATLAB_KO;
	     break;
      case miUINT16:
	     Depth=16;		  /*Word type cell*/
	     ldblk=(long) (2*MATLAB_HDR.SizeX);
	     if(MATLAB_HDR.StructureFlagClass & FLAG_COMPLEX) goto FINISH; //MATLAB_KO;
#ifdef LO_ENDIAN
             if(MATLAB_HDR.idx[0]=='M') swabXX=swab16;
#endif
#ifdef HI_ENDIAN
             if(MATLAB_HDR.idx[0]=='I') swabXX=swab16;
#endif
	     break;
      case miUINT32:
	     Depth=32;		  /*DWord type cell*/
	     ldblk=(long) (4*MATLAB_HDR.SizeX);
	     // BUG! endians must be swapped
	     if(MATLAB_HDR.StructureFlagClass & FLAG_COMPLEX) goto FINISH; //MATLAB_KO;
#ifdef LO_ENDIAN
             if(MATLAB_HDR.idx[0]=='M') swabXX=swab32;
#endif
#ifdef HI_ENDIAN
             if(MATLAB_HDR.idx[0]=='I') swabXX=swab32;
#endif
	     break;
      case miSINGLE:Depth=-32;	//float type cell
	     if(sizeof(float)!=4) goto FINISH;
	     if(MATLAB_HDR.StructureFlagClass & FLAG_COMPLEX)
		  {      	//complex double type cell
		  goto FINISH; //MATLAB_KO;
		  }
	     if(z>=3) goto FINISH; //MATLAB_KO;
	     ldblk = (long)4*MATLAB_HDR.SizeX;
#ifdef LO_ENDIAN
             if(MATLAB_HDR.idx[0]=='M') swabXX=swab32;
#endif
#ifdef HI_ENDIAN
             if(MATLAB_HDR.idx[0]=='I') swabXX=swab32;
#endif
	     break;
      case miDOUBLE:Depth=-64;	//double type cell
	     if(sizeof(double)!=8) goto FINISH;
	     if(MATLAB_HDR.StructureFlagClass & FLAG_COMPLEX)
		  {      	//complex double type cell
		  goto FINISH; //MATLAB_KO;
		  }
	     if(z>=3) goto FINISH; //MATLAB_KO;
	     ldblk = (long)8*MATLAB_HDR.SizeX;
#ifdef LO_ENDIAN
             if(MATLAB_HDR.idx[0]=='M') swabXX=swab64;
#endif
#ifdef HI_ENDIAN
             if(MATLAB_HDR.idx[0]=='I') swabXX=swab64;
#endif
	     break;
      default:goto FINISH;
      }

  if(MATLAB_HDR.SizeX==0 || MATLAB_HDR.SizeY==0 || Depth==0)
	goto FINISH;

  if(z==3)
    Raster = CreateRaster2DRGB(MATLAB_HDR.SizeY,MATLAB_HDR.SizeX,8);
  else
    Raster = CreateRaster2D(MATLAB_HDR.SizeY,MATLAB_HDR.SizeX,Depth);
  if(Raster==NULL) goto FINISH;

  if(z==3)
    {
    Raster1DAbstract *Ras=NULL;
    RBit8.Allocate1D(MATLAB_HDR.SizeX);
    if(RBit8.Data1D==NULL) goto FINISH;
    if(Depth!=8)
      {
      Ras = CreateRaster1D(MATLAB_HDR.SizeX,Depth);
      if(Ras==NULL) goto FINISH_LOOP;
      }

    while(z>0)
      {
      for(iy=0;iy<MATLAB_HDR.SizeY;iy++)
	{
	if(Depth==8)
	  {if(fread(RBit8.Data1D,MATLAB_HDR.SizeX,1,f)!=1) goto FINISH_LOOP;}
	else
	  {
	  if(fread(Ras->Data1D,ldblk,1,f)!=1) goto FINISH_LOOP;
	  Ras->Get(RBit8);
	  }

	  /* Rotate image data by 90 degs */
	int shift = Raster->GetPlanes()-8*z;
	uint32_t mask = ~(0xFF<<shift);
	for(ix=0;ix<MATLAB_HDR.SizeX;ix++)
	  {
	  Raster->SetValue2D(iy,ix, (Raster->GetValue2D(iy,ix)&mask)|(uint32_t)(RBit8.GetValue1D(ix))<<shift);
	  }
	}
      z--;
      }
FINISH_LOOP:
    if(Ras) delete(Ras);
    }
  else
    {
    RBit8.Allocate1D(ldblk);
    if(RBit8.Data1D==NULL) goto FINISH;

    for(iy=0; iy<MATLAB_HDR.SizeY; iy++)
      {
      if(fread(RBit8.Data1D,ldblk,1,f)!=1)
		goto FINISH;

      if(swabXX) swabXX((uint8_t*)RBit8.Data1D,MATLAB_HDR.SizeX); //RBit8.Size contains byte count.

	/* Rotate image data by 90 degs */
      for(ix=0; ix<MATLAB_HDR.SizeX; ix++)
	{
	switch(Depth)
	  {
	  case 1:  Raster->SetValue2D(iy,ix,((uint8_t *)(RBit8.Data1D))[ix]?1:0); break;
	  case 8:  Raster->SetValue2D(iy,ix,((uint8_t *)(RBit8.Data1D))[ix]); break;
	  case 16: Raster->SetValue2D(iy,ix,((uint16_t *)(RBit8.Data1D))[ix]); break;
	  case 32: Raster->SetValue2D(iy,ix,((uint32_t *)(RBit8.Data1D))[ix]); break;
	  case -32:Raster->SetValue2Dd(iy,ix,((float *)(RBit8.Data1D))[ix]); break;
	  case -64:Raster->SetValue2Dd(iy,ix,((double *)(RBit8.Data1D))[ix]); break;
	  }
	}
      }
    }

FINISH:
  if(RBit8.Data1D) RBit8.Erase();

  fclose(f);
  Img.AttachRaster(Raster);
  return(Img);
}


typedef struct {
	uint8_t Type[4];
	uint32_t nRows;
	uint32_t nCols;
	uint32_t imagf;
	uint32_t nameLen;
} MAT4_HDR;


Image LoadPictureMAT4(const char *Name)
{
Image Img;
Raster2DAbstract *Raster;
FILE *f;
MAT4_HDR HDR;
char Depth;
long ldblk;
unsigned ix;

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

  if(fread(&HDR.Type,4,1,f)!=1) goto ENDPROC;
  if(HDR.Type[3]!=0) goto ENDPROC;	// Data format
  if(HDR.Type[2]!=0) goto ENDPROC;	// Always 0
  switch(HDR.Type[1])
  {
    case 0: Depth = -64; break; 	// double-precision (64-bit) floating point numbers
    case 1: Depth = -32; break;		// single-precision (32-bit) floating point numbers
    case 2: Depth = 32; break;		// 32-bit signed integers
    case 3: Depth = 16; break;		// 16-bit signed integers
    case 4: Depth = 16; break;		// 16-bit unsigned integers
    case 5: Depth = 8; break;		// 8-bit unsigned integers
    default: return(Img);
  }

  if(HDR.Type[0]!=0) return(Img);	// Only numeric matrices supported.

  RdDWORD_LoEnd(&HDR.nRows,f);
  RdDWORD_LoEnd(&HDR.nCols,f);

  RdDWORD_LoEnd(&HDR.imagf,f);
  if(HDR.imagf!=0 && HDR.imagf!=1) return(Img);

  RdDWORD_LoEnd(&HDR.nameLen,f);
  if(HDR.nameLen>0xFFFF) return(Img);

  fseek(f, HDR.nameLen, SEEK_CUR);

  Raster = CreateRaster2D(HDR.nRows,HDR.nCols,Depth);
  if(Raster==NULL) {fclose(f); return Img;}

  ldblk = labs(Depth)/8 * HDR.nRows;

  for(ix=0; ix<Raster->Size2D; ix++)
    {
    if(fread(Raster->GetRow(ix),ldblk,1,f)!=1) {goto ENDPROC;}
    //AlineProc(i,p);
    }


  Img.AttachRaster(Raster);

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

#endif


#if SupportMAT>=3

static const char *MonthsTab[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
static const char *DayOfWTab[7]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
#if defined(_WIN32) || defined(__WIN32__) || defined(__DOS__) || defined(__DJGPP__)
 static const char *OsDesc="PCWIN";
#else
 static const char *OsDesc="LNX86";
#endif

/** This procedure fills common stuff into the matlab header. */
static void BuildMAT_TXT(char *Header)
{
  const struct tm *t;
  time_t current_time;

  current_time=time((time_t *) NULL);
  t=localtime(&current_time);
  sprintf(Header,"MATLAB 5.0 MAT-file, Platform: %s, Created on: %s %s %2d %2d:%2d:%2d %d",
    OsDesc,
    DayOfWTab[t->tm_wday],
    MonthsTab[t->tm_mon],
    t->tm_mday,
    t->tm_hour,t->tm_min,t->tm_sec,
    t->tm_year+1900);
  Header[0x7D]=1;
  *(uint16_t*)(Header+0x7E) = 'I' + 256*'M';	// This will work on both endian architectures.
  //Header[0x7E]='I';  Intel low endian result.
  //Header[0x7F]='M';
}


/**This procedure saves image data into MATLAB MAT file.
 * Please note that MAT file is stored in native endian. */
int SavePictureMAT(const char *Name, const Image &Img)
{
FILE *f;
uint16_t iy;
int8_t padding=0;
int IsColor=0;

  if(Img.Raster==NULL) return(ErrEmptyRaster);
  if((f=fopen(Name,"wb"))==NULL) return(ErrOpenFile);

  IsColor = Img.Raster->GetPlanes()==24;

  char MATLAB_HDR[184];
  uint32_t DataSize;

  DataSize = (uint32_t)Img.Raster->GetSize1D() * (uint32_t)Img.Raster->Size2D;

  memset(MATLAB_HDR,' ',124);
  memset(MATLAB_HDR+124,0,sizeof(MATLAB_HDR)-124);

  BuildMAT_TXT(MATLAB_HDR);

  *(uint32_t *)&(MATLAB_HDR[0x80]) = miMATRIX;
  *(uint32_t *)&(MATLAB_HDR[0x88]) = 0x6;
  *(uint32_t *)&(MATLAB_HDR[0x8C]) = mxUINT8_CLASS;
  *(uint32_t *)&(MATLAB_HDR[0x90]) = 0x6;
  *(uint32_t *)&(MATLAB_HDR[0x98]) = 0x5;
  *(uint32_t *)&(MATLAB_HDR[0x9C]) = IsColor?12:0x8;         //DimFlag
  *(uint32_t *)&(MATLAB_HDR[0xA0])=Img.Raster->Size2D;  //Rotate image 90 degs
  *(uint32_t *)&(MATLAB_HDR[0xA4])=Img.Raster->GetSize1D();  //Rotate image 90 degs
  *(uint16_t *)&(MATLAB_HDR[0xA8]) = 1;
  *(uint16_t *)&(MATLAB_HDR[0xAA]) = 1;   		//NameFlag
  *(uint32_t *)&(MATLAB_HDR[0xAC]) = 'M';		//matrix name
  if(IsColor)
    {
    *(uint32_t *)&(MATLAB_HDR[0xB0]) = miUINT8;	//uint8_t
    DataSize *= 3;
    }
  else
    {
    switch(Img.Raster->GetPlanes())
      {
      case  1:
      case  2:
      case  4:
      case  8: *(uint32_t *)&(MATLAB_HDR[0xB0]) = miUINT8;
	       *(uint32_t *)&(MATLAB_HDR[0x8C]) = mxUINT8_CLASS;
	       break;					//BYTE
      case 16: *(uint32_t *)&(MATLAB_HDR[0xB0]) = miUINT16;
	       *(uint32_t *)&(MATLAB_HDR[0x8C]) = mxUINT16_CLASS;
	       DataSize *= 2;
	       break;					//uint16_t
      case 24:
      case 32: *(uint32_t *)&(MATLAB_HDR[0xB0]) = miUINT32;
	       *(uint32_t *)&(MATLAB_HDR[0x8C]) = mxUINT32_CLASS;
	       DataSize *= 4;
	       break;					//DWORD
      case -32:*(uint32_t *)&(MATLAB_HDR[0xB0]) = miSINGLE;
	       *(uint32_t *)&(MATLAB_HDR[0x8C]) = mxSINGLE_CLASS;
	       DataSize *= 4;
	       break;
      case -64:*(uint32_t *)&(MATLAB_HDR[0xB0]) = miDOUBLE;
	       *(uint32_t *)&(MATLAB_HDR[0x8C]) = mxDOUBLE_CLASS;
	       DataSize *= 8;
	       break;
      default: goto FINISH;
      }
    }

  padding = ((uint8_t)(DataSize-1) & 0x7) ^ 0x7;
  *(uint32_t *)&(MATLAB_HDR[0x84]) = DataSize + 56l + (uint32_t)padding;

  *(uint32_t *)&(MATLAB_HDR[0xB4]) = DataSize;

  unsigned ix;
  if(IsColor)
    {
    fwrite(MATLAB_HDR,1,sizeof(MATLAB_HDR)-16,f);
    DataSize = 3;       //z order
    fwrite(&DataSize,1,4,f);	// use native endian
    DataSize = 0;       //Unknown 5
    fwrite(&DataSize,1,4,f);	// use native endian
    fwrite(MATLAB_HDR+sizeof(MATLAB_HDR)-16,1,16,f); // co to je????? !!!!

    uint8_t *ptrLineX;
    ptrLineX = (uint8_t *)malloc(Img.Raster->Size2D);  // traverse by y

    for(IsColor=0;IsColor<=16;IsColor+=8)
      {
      for(ix=0; ix<Img.Raster->GetSize1D();ix++)
	{
	for(iy=0;iy<Img.Raster->Size2D;iy++)
	  {
	  ptrLineX[iy] = ((uint32_t)Img.GetPixel(ix,iy)>>IsColor) & 0xFF;
	  }
	fwrite(ptrLineX,Img.Raster->Size2D,1,f);
	}
      }
    if(ptrLineX) {free(ptrLineX);ptrLineX=NULL;}
    }
  else
    {
    fwrite(MATLAB_HDR,1,sizeof(MATLAB_HDR),f);

    switch(*(uint32_t*)&(MATLAB_HDR[0xB0]))
    {
      case miUINT8:
	{
	uint8_t *ptrLineX;
	ptrLineX = (uint8_t *)malloc(Img.Raster->Size2D);   // traverse by y
	if(ptrLineX==NULL) break;
	for(ix=0; ix<Img.Raster->GetSize1D();ix++)
	  {
	  for(iy=0;iy<Img.Raster->Size2D;iy++)
	    {
	    ptrLineX[iy] = Img.GetPixel(ix,iy); //Gray image 8 bit, rotated 90 degs
	    }
	  fwrite(ptrLineX,Img.Raster->Size2D,1,f);
	  }
	if(ptrLineX) {free(ptrLineX);ptrLineX=NULL;}
	break;
	}
      case miUINT16:
	{
	uint16_t *ptrLineX;

	ptrLineX = (uint16_t *)malloc(2*Img.Raster->Size2D);
	if(ptrLineX==NULL) break;
	for(ix=0; ix<Img.Raster->GetSize1D();ix++)
	  {
	  for(iy=0;iy<Img.Raster->Size2D;iy++)
	    {
	    ptrLineX[iy] = Img.GetPixel(ix,iy); //Gray image 16 bit, rotated 90 degs
	    }

	  fwrite(ptrLineX,2*Img.Raster->Size2D,1,f);
	  }
	if(ptrLineX) {free(ptrLineX);ptrLineX=NULL;}
	break;
	}
      case miSINGLE:
	{
	float *ptrLineX;

	ptrLineX = (float *)malloc(sizeof(float)*Img.Raster->Size2D);
	if(ptrLineX==NULL) break;
	for(ix=0; ix<Img.Raster->GetSize1D();ix++)
	  {
	  for(iy=0;iy<Img.Raster->Size2D;iy++)
	    {
	    ptrLineX[iy] = Img.Raster->GetValue2Dd(ix,iy); //float image 32 bit, rotated 90 degs
	    }

	  fwrite(ptrLineX,sizeof(float)*Img.Raster->Size2D,1,f);
	  }
	if(ptrLineX) {free(ptrLineX);ptrLineX=NULL;}
	break;
	}
      case miDOUBLE:
	{
	double *ptrLineX;

	ptrLineX = (double *)malloc(sizeof(double)*Img.Raster->Size2D);
	if(ptrLineX==NULL) break;
	for(ix=0; ix<Img.Raster->GetSize1D();ix++)
	  {
	  for(iy=0;iy<Img.Raster->Size2D;iy++)
	    {
	    ptrLineX[iy] = Img.Raster->GetValue2Dd(ix,iy); //double image 64 bits, rotated 90 degs
	    }

	  fwrite(ptrLineX,sizeof(double)*Img.Raster->Size2D,1,f);
	  }
	if(ptrLineX) {free(ptrLineX);ptrLineX=NULL;}
	break;
	}
      }
    }

  while(padding-->0) fputc(0,f);
FINISH:
  fclose(f);
  return(0);
}
#endif

#endif
//-------------------End of MAT routines------------------


//--------------------------OKO---------------------------
#ifdef SupportOKO

typedef struct
	{
	char  identific[80];
	uint16_t  ver;
	uint16_t  modif;
	uint16_t  Histogram;
	uint16_t  Text;
	uint16_t  rows;
	uint16_t  cols;
	uint16_t  levels;
	uint16_t  identif;
	char dummy[416];
	}OkoHeader;


#if SupportOKO>=4 || SupportOKO==2

inline long LoadOkoHeader(FILE *f, OkoHeader &HDR)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(SU),f));
#else
 return(loadstruct(f,"a80wwwwwwwwa416",
	 &HDR.identific, &HDR.ver, &HDR.modif,
	 &HDR.Histogram, &HDR.Text, &HDR.rows, &HDR.cols, &HDR.levels,
	 &HDR.identif, &HDR.dummy));
#endif
}


Image LoadPictureOKO(const char *Name)
{
FILE  *f;
uint16_t ldblk;
uint16_t i;
Raster2DAbstract *Raster=NULL;
Image Img;
OkoHeader Header;

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

  if(LoadOkoHeader(f, Header) != 512) goto ENDPROC;
  if(strncmp(Header.identific,"OBR2 Image File",15)) goto ENDPROC;

  if(Header.levels==0) goto ENDPROC;	//wrong image
  //printf(" %d %f \n\r",Header.levels,(log((float)(Header.levels+1))/log(2.0))+1e-3);
  Raster=CreateRaster2D(Header.rows,Header.cols,(int)(log((float)(Header.levels+1))/log(2.0)+1e-3));
  if(Raster==NULL) goto ENDPROC;

// if AlineProc<>nil then AlineProc^.InitPassing(p.y,'Loading OKO');
  fseek(f,0x200,SEEK_SET);

  ldblk=(Raster->GetPlanes()*Raster->GetSize1D()+7)/8;
  for(i=0;i<Raster->Size2D;i++)
	{
	if(fread(Raster->GetRow(i),ldblk,1,f)!=1) {goto ENDPROC;}
 //	AlineProc(i,p);
	}

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


#if SupportOKO>=3

inline long SaveOkoHeader(FILE *f, const OkoHeader &HDR)
{
#if defined(__PackedStructures__)
 return(fwrite(&SU,1,sizeof(SU),f));
#else
 return(savestruct(f,"a80wwwwwwwwa416",
	 HDR.identific, HDR.ver, HDR.modif,
	 HDR.Histogram, HDR.Text, HDR.rows, HDR.cols, HDR.levels,
	 HDR.identif, HDR.dummy));
#endif
}


int SavePictureOKO(const char *Name, const Image &Img)
{
FILE *f;
uint16_t ldblk;
uint16_t i;
OkoHeader Header;
Raster1DAbstract *ConvertMe=NULL;
int Result = 0;

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

  memset(&Header,0,sizeof(Header));

  Header.rows=Img.Raster->GetSize1D();
  Header.cols=Img.Raster->Size2D;
  Header.ver=3;
  Header.modif=5;
  if(Img.Raster->GetPlanes()>=16 || Img.Raster->GetPlanes()<0)
  {
    Header.levels = 0xFFFF;
    ldblk = Header.rows * 2l;
    if(Img.Raster->GetPlanes()>16 ||  	//OKO cannot store more than 16bits
       Img.Raster->GetPlanes()<0 )      //OKO cannot store float values
	 ConvertMe = CreateRaster1D(Img.Raster->GetSize1D(),16);
  }
  else
  {
    Header.levels = 1 << Img.Raster->GetPlanes();
    ldblk = ((long)Img.Raster->GetPlanes()*Header.rows+7) / 8;
  }
  strcpy(Header.identific,"OBR2 Image File v3.05");

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

  if(SaveOkoHeader(f,Header) != 512) 
  {
    Result = ErrWriteFile;
    goto FINISH;
  }

  for(i=0;i<Header.cols;i++)
  {
    if(ConvertMe!=NULL)
    {
      ConvertMe->Set(*Img.Raster->GetRowRaster(i));
      fwrite(ConvertMe->Data1D,ldblk,1,f);
    }
    else
        fwrite(Img.Raster->GetRow(i),ldblk,1,f);
        // if AlineProc<>nil then AlineProc^.NextLine;
  }

FINISH:
  fclose(f);
  if(ConvertMe) {delete ConvertMe; ConvertMe=NULL;}
  return(Result);
}
#endif

#endif
//-------------------End of OKO routines------------------


//-----------------------PBM------------------------------
#ifdef SupportPBM

// https://netpbm.sourceforge.net/doc/ppm.html

#if SupportPBM>=4 || SupportPBM==2

Image LoadPicturePBM(const char *Name)
{
FILE *f;
uint16_t x,y;
char ch;
char a[255];
int binary;
long i;
Raster2DAbstract *Raster=NULL;
Image Img;
Image *CurImg = &Img;

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

 while(!feof(f))
 {
   ch=' ';
   ReadWord(f,a,sizeof(a),&ch);

   if(*a!='P') goto ENDPROC;
   if(strlen(a)!=2) goto ENDPROC;
   const char TypePxM = a[1]-'0';

   do
   {
     ReadWord(f,a,sizeof(a),&ch);
     if(a[0]=='#') readln(f);
     if(feof(f)) goto ENDPROC;
   } while(a[0]=='#');
   if(*a==0) goto ENDPROC;

   x = atol(a);
   switch(TypePxM)
   {
     case 1: y=ReadInt(f,&ch);
	     binary=0;
	     if(x<=0 || y<=0) break;
	     Raster=CreateRaster2D(x,y,1);
	     break;
     case 2: y=ReadInt(f,&ch);
	     i=ReadInt(f,&ch);
	     binary=0;
	     if(x<=0 || y<=0) break;
	     if(i<=0 || i>=65536) break;	// Raster is NULL on break and it will abort reading.
	     if(i<16)
               Raster=CreateRaster2D(x,y,4);
             else if(i<256)
               Raster=CreateRaster2D(x,y,8);
             else
               Raster=CreateRaster2D(x,y,16);
	     break;
     case 3:					// true color text
	     y = ReadInt(f,&ch);
	     i = ReadInt(f,&ch);	// The maximum color value (Maxval). Must be less than 65536.
	     binary = 0;
	     if(x<=0 || y<=0) break;
	     if(i<=0 || i>=65536) break;
	     Raster = CreateRaster2DRGB(x,y,(i>255)?16:8);
	     break;
     case 4: y=ReadInt(f,&ch);
	     if(x<=0 || y<=0) break;
	     Raster=CreateRaster2D(x,y,1);
	     binary=1;
	     break;
     case 5:					//gray level
	     y=ReadInt(f,&ch);
	     if(x<=0) break;
	     i=ReadInt(f,&ch);
	     binary=1;
	     if(i<=0 || i>=65536) break;
             if(i<16)
               Raster=CreateRaster2D(x,y,4);
             else if(i<256)
               Raster=CreateRaster2D(x,y,8);
             else
               Raster=CreateRaster2D(x,y,16);
	     break;
     case 6:				//binary true color 24bit format
	     y = ReadInt(f,&ch);
	     i = ReadInt(f,&ch);
	     if(i!=255) goto ENDPROC;
	     if(x<=0) goto ENDPROC;
	     Raster = CreateRaster2DRGB(x,y,8);
	     binary = 1;
	     break;
     default: goto ENDPROC;
     }

   if(Raster==NULL || Raster->Data2D==NULL) goto ENDPROC;

 //if AlineProc<>nil then
 // 	AlineProc^.InitPassing(p.y,'Loading PBM');

   y = 0;
   x = 0;
   if(binary!=0)		//Binary modification
	{
	i=(long)(Raster->GetPlanes()*Raster->GetSize1D()+7) / 8;
	for(y=0;y<Raster->Size2D;y++)
	  {
	  if(fread(Raster->GetRow(y),i,1,f)!=1) break;
	  if(Raster->GetPlanes()==1)
	    NotR((char *)Raster->GetRow(y),i);
 //	  if(AlineProc) AlineProc(i,p);
	  }
	}
     else {		//Text modification
	ch=' ';
	while(!feof(f)) 	//load picture data
	  {
	  if(ch=='#') {readln(f);ch='\13';continue;}
	  if(isspace(ch)) {ch=fgetc(f);continue;}
	  if(isalpha(ch)) break;	//unexpected character occured

	  i=ReadInt(f,&ch);
	  if(ch=='.') {ch=0; ReadInt(f,&ch);}	//dummy read frac part
	  if(Raster->GetPlanes()==24)
	    {
	    i+=256*ReadInt(f,&ch);
	    if(ch=='.') {ch=0; ReadInt(f,&ch);}	//dummy read frac part
	    i+=65536*ReadInt(f,&ch);
	    if(ch=='.') {ch=0; ReadInt(f,&ch);} //dummy read frac part
	    }

	  Raster->SetValue2D(x,y,i);
	  x++;
	  if(x>=Raster->GetSize1D())
	    {
	    x=0;
	    if(Raster->GetPlanes()==1)  //binary images are inverted
	      NotR((char *)Raster->GetRow(y),(Raster->GetSize1D()+7)/8);
	    y++;
	    if(y>=Raster->Size2D) break;
	    }
	  }
	}

    if(Raster!=NULL)
    {
      if(CurImg->Raster!=NULL)
      {
        CurImg->Next = new Image;
        CurImg = CurImg->Next;
      }
      CurImg->AttachRaster(Raster);
      Raster = NULL;
    }
  }
ENDPROC:
  fclose(f);
  if(Raster!=NULL)
  {
    delete Raster;
    Raster = NULL;
  }
  return(Img);
}  //LoadPBM
#endif


#if SupportPBM>=3


int SavePicturePGM(const char *Name, const Image &Img)
{
FILE *F;
unsigned x, y;
Raster1DAbstract *pRas1D;
  if(Img.Raster==NULL) return(ErrEmptyRaster);
  if((F=fopen(Name,"wb"))==NULL) return(ErrOpenFile);

  fprintf(F,"P2\n# %s\n%u %u\n", Name, Img.Raster->Size1D, Img.Raster->Size2D);
  switch(Img.Raster->GetPlanes())
  {
    case 1: fputs("1",F); break;
    case 2: fputs("3",F); break;
    case 4: fputs("15",F); break;
    case 8: fputs("255",F); break;
    case 16: fputs("65535",F); break;
    default: goto ENDPROC;
  }

  if(Img.Raster->Size1D<=0 || Img.Raster->Size2D<=0)
    fputc('\n',F);
  else
  {
    for(y=0; y<Img.Raster->Size2D; y++)
    {
      fputc('\n',F);
      pRas1D = Img.Raster->GetRowRaster(y);
      if(pRas1D==NULL) continue;

      fprintf(F,"%u",pRas1D->GetValue1D(0));
      for(x=1; x<Img.Raster->Size1D; x++)
      {
        fprintf(F," %u",pRas1D->GetValue1D(x));
      }
    }
  }

ENDPROC:
  fclose(F);
  return 0;
}


int SavePicturePBM(const char *Name, const Image &Img)
{
FILE *F;
unsigned y, RowSize;
int RetVal = ErrOK;

  if(Img.Raster==NULL) return(ErrEmptyRaster);
  if((F=fopen(Name,"wb"))==NULL) return(ErrOpenFile);

  if(Img.Raster->Size1D>0 && Img.Raster->Size2D>0)
  {
    fprintf(F,"P4\n# %s\n%u %u\n", Name, Img.Raster->Size1D, Img.Raster->Size2D);
    Raster1DAbstract *pRas1Dbin = CreateRaster1D(Img.Raster->Size1D,1);
    if(pRas1Dbin==NULL) {fclose(F);return ErrNoMem;}

    RowSize = (Img.Raster->Size1D+7) / 8;

    for(y=0; y<Img.Raster->Size2D; y++)
    {
      const Raster1DAbstract *pRas1D = Img.Raster->GetRowRaster(y);
      if(pRas1D==NULL) {RetVal=ErrAccessData; continue;}
      pRas1Dbin->Set(*pRas1D);
      if(Img.Raster->GetPlanes() == 1)
          NotR((char*)pRas1Dbin->Data1D, RowSize);
      fwrite(pRas1Dbin->Data1D, RowSize,1, F);
    }

    delete pRas1Dbin;
  }

  fclose(F);
  return RetVal;
}


int SavePicturePPM(const char *Name, const Image &Img)
{
FILE *F;
unsigned y, RowSize;
Raster1DAbstractRGB *pRas1DRGB = NULL;

  if(Img.Raster==NULL) return(ErrEmptyRaster);
  if((F=fopen(Name,"wb"))==NULL) return(ErrOpenFile);

  fprintf(F,"P6\n# %s\n%u %u\n", Name, Img.Raster->Size1D, Img.Raster->Size2D);
  switch(Img.Raster->GetPlanes())
  {
    case 24: if(Img.Raster->Channels() != 3)
              pRas1DRGB = CreateRaster1DRGB(Img.Raster->Size1D,8);
    case 1:
    case 2:
    case 4:
    case 8:  fputs("255\n",F);
             RowSize = 3*Img.Raster->Size1D;
             break;
    case 16: fputs("65535\n",F);
             if(Img.Raster->Channels() != 3)
              pRas1DRGB = CreateRaster1DRGB(Img.Raster->Size1D,16);
             RowSize = 6*Img.Raster->Size1D;
             break;
    default: goto ENDPROC;
  }

  if(Img.Raster->Size1D>0 && Img.Raster->Size2D>0)
  {
    for(y=0; y<Img.Raster->Size2D; y++)
    {
      if(pRas1DRGB==NULL)
        fwrite(Img.Raster->GetRowRaster(y)->Data1D,1, RowSize,F);
      else
      {
        const Raster1DAbstract *pRas1D = Img.Raster->GetRowRaster(y);
        if(pRas1D==NULL) continue;
        pRas1DRGB->Set(*pRas1D);
        fwrite(pRas1DRGB->Data1D, RowSize,1, F);
      }
    }
  }

ENDPROC:
  if(pRas1DRGB) delete pRas1DRGB;
  fclose(F);
  return 0;
}


#endif


#endif
//-------------------End of PBM routines------------------


//-----------------------PDB------------------------------
#ifdef SupportPDB

typedef struct
{
  char name[32];
  uint16_t attributes;
  uint16_t version;
  uint32_t create_time;
  uint32_t modify_time;
  uint32_t archive_time;
  uint32_t modify_number;
  uint32_t application_info;
  uint32_t sort_info;
  char type[4];			///< database type identifier "vIMG".
  char id[4];			///< database creator identifier "View".
  uint32_t seed;
  uint32_t next_record;
  uint16_t number_records;
} PdbHeader;

typedef struct
{
  char		name[32];
  uint8_t	version;
  uint8_t	type;
  uint32_t	reserved_1;
  uint32_t	note;
  uint16_t	x_last;
  uint16_t	y_last;
  uint32_t	reserved_2;
  uint16_t	x_anchor;
  uint16_t	y_anchor;
  uint16_t	width;
  uint16_t	height;
} PDBImage;


static const unsigned char RecHeader[4] = {0x40, 0x6f, 0x80, 0};
static const unsigned char RecHeaderCom[4] = {0x40, 0x6f, 0x80, 1};


#if SupportPDB>=4 || SupportPDB==2


inline long LoadPdbHeader(FILE *f, PdbHeader &HDR)
{
#if defined(__PackedStructures__) && defined(HI_ENDIAN)
 return(fread(&HDR,1,sizeof(HDR),f));
#else
 return(loadstruct(f,"a32WWDDDDDDa4a4DDW",
	HDR.name, &HDR.attributes, &HDR.version,
	&HDR.create_time, &HDR.modify_time, &HDR.archive_time, &HDR.modify_number, &HDR.application_info, &HDR.sort_info,
	HDR.type, HDR.id,
        &HDR.seed, &HDR.next_record, &HDR.number_records));
#endif
}

inline long LoadPdbImage(FILE *f, PDBImage &IMG)
{
#if defined(__PackedStructures__) && defined(HI_ENDIAN)
 return(fread(&IMG,1,sizeof(IMG),f));
#else
 return(loadstruct(f,"a32bbDDWWDWWWW",
	IMG.name, &IMG.version, &IMG.type,
        &IMG.reserved_1, &IMG.note, &IMG.x_last, &IMG.y_last, &IMG.reserved_2,
        &IMG.x_anchor, &IMG.y_anchor, &IMG.width, &IMG.height));
#endif
}


Image LoadPicturePDB(const char *Name)
{
FILE *f;
Image Img;
PdbHeader HDR;
PDBImage IMG;
uint32_t img_offset;
uint8_t tag[4];
uint8_t bits_per_pixel;
unsigned y;

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

 if(LoadPdbHeader(f,HDR)!=78) return Img;
 if(memcmp(HDR.type,"vIMG",4)!=0 || memcmp(HDR.id,"View",4)!=0)
     goto ExitErr;

 if(RdDWORD_HiEnd(&img_offset,f)!=4) goto ExitErr;

 if(fread(tag,1,4,f) != 4) goto ExitErr;
 if(memcmp(tag,RecHeader,3)!=0 || (tag[3]!=0 && tag[3]!=1)) goto ExitErr;

 if(img_offset-ftell(f)==6)
 {
   fseek(f,2,SEEK_CUR);
 }
 else if(HDR.number_records > 1)
 {
   if(RdDWORD_HiEnd(&img_offset,f)!=4) goto ExitErr;
   if(fread(tag,1,4,f) != 4) goto ExitErr;
   if(memcmp(tag,RecHeader,3)!=0 || (tag[3]!=0 && tag[3]!=1)) goto ExitErr;
   
   if((img_offset-ftell(f)) == 6)
   {
     fseek(f,2,SEEK_CUR);
   }
 }

 if(LoadPdbImage(f,IMG)!=58) goto ExitErr;
 //printf("ftell(f) %u ",ftell(f));

 if((IMG.version&7) >= 2) goto ExitErr;
 
 switch(IMG.type)
 {
   case 0: bits_per_pixel=2; break;
   case 2: bits_per_pixel=4; break;
   default: bits_per_pixel=1; break;
 } 
 
 Img.AttachRaster(CreateRaster2D(IMG.width,IMG.height,bits_per_pixel));
 if(Img.Raster==NULL || Img.Raster->Size1D==0 || Img.Raster->Size2D==0)
     goto ExitErr;

 {
   const unsigned Ldblk = (Img.Raster->Size1D*Img.Raster->GetPlanes() + 7) / 8;
   if((IMG.version&7)==0)
   {   
     for(y=0; y<IMG.height; y++)
     {
       if(fread(Img.Raster->GetRow(y),1,Ldblk,f) != Ldblk)
       {
         break;      
       }
     }
   }
   else
   {
     uint8_t *pRow;
     unsigned ByteX;
     uint8_t RunCount = 0xFF;
     uint8_t PxByte;
     ByteX = y = 0;
     pRow = (uint8_t*)Img.Raster->GetRow(0);
     while(y < Img.Raster->Size2D)
     {     
       RunCount = fgetc(f);
       if(RunCount <= 0x80)
       {
         RunCount++;
         while(RunCount-- > 0)
         {
           pRow[ByteX++] = ~fgetc(f);         
           if(ByteX>=Ldblk)
           {
             ByteX = 0;
	     if(++y>=Img.Raster->Size2D) break;
             pRow = (uint8_t*)Img.Raster->GetRow(y);
           }
         }
       }
       else
       {
         RunCount = RunCount-0x80 + 1;
         PxByte = fgetc(f);
         while(RunCount-- > 0)
         {
           pRow[ByteX++] = ~PxByte;
           if(ByteX>=Ldblk)
           {
             ByteX = 0;
	     if(++y>=Img.Raster->Size2D) break;
             pRow = (uint8_t*)Img.Raster->GetRow(y);
           }
         }
       }
     }
   }
 }

#ifndef NO_EXTENSIONS
 if(!feof(f) && (img_offset-ftell(f))==0)
 {   
   char comment[16];
   PropertyItem *Prop = new PropertyItem("comment");
   if(Prop)
   {
     size_t length = 0;     
     int c = 1;
     while(c!=0)
     {
       c = fgetc(f);
       if(c==EOF) c=0;
       if(length>=sizeof(comment))
       {
         Prop->Append(comment,sizeof(comment));
         length=0;
       }
       comment[length++] = c;
     }
     Prop->Append(comment,length);
     if(Prop->Data)
       Img.Properties.AddProp(Prop);
     else
         delete(Prop);
   }
 }
#endif

 fclose(f);
 return(Img);

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

#endif


#if SupportPDB>=3


inline long SavePdbHeader(FILE *f, const PdbHeader &HDR)
{
#if defined(__PackedStructures__) && defined(HI_ENDIAN)
 return(fwrite(&HDR,1,sizeof(HDR),f));
#else
 return(savestruct(f,"a32WWDDDDDDa4a4DDW",
	HDR.name, HDR.attributes, HDR.version,
	HDR.create_time, HDR.modify_time, HDR.archive_time, HDR.modify_number, HDR.application_info, HDR.sort_info,
	HDR.type, HDR.id,
        HDR.seed, HDR.next_record, HDR.number_records));
#endif
}

inline long SavePdbImage(FILE *f, const PDBImage &IMG)
{
#if defined(__PackedStructures__) && defined(HI_ENDIAN)
 return(fwrite(&IMG,1,sizeof(IMG),f));
#else
 return(savestruct(f,"a32bbDDWWDWWWW",
	IMG.name, IMG.version, IMG.type,
        IMG.reserved_1, IMG.note, IMG.x_last, IMG.y_last, IMG.reserved_2,
        IMG.x_anchor, IMG.y_anchor, IMG.width, IMG.height));
#endif
}


static uint8_t *EncodeRLE(uint8_t *destination, const uint8_t *source, unsigned literal, unsigned repeat)
{
  if(literal != 0)
  {
    *destination++ = literal-1;
    memcpy(destination,source,literal);
    destination += literal;
  }
  if(repeat != 0)
  {
    *destination++ = 0x80 | (repeat-1);
    *destination++ = source[literal];
  }
  return(destination);
}


int SavePicturePDB(const char *Name, const Image &Img)
{
FILE *F;
int result;
PDBImage IMG;
PdbHeader HDR;
uint8_t *Buffer = NULL;

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

  memset(&IMG,0,sizeof(IMG));  
  switch(labs(Img.Raster->GetPlanes()))
  {
    case 1: IMG.type=0xFF; break;		// monochrome.
    case 2: IMG.type=0x00; break;		// 2 bit gray.
    case 4: IMG.type=0x02; break;		// 4 bit gray.
    default: return ErrUnsupportedResolution;
  }

  result = 0;
  const PropertyItem *PropComment = Img.Properties.Find("comment", *(unsigned*)&result);
  result = 0;

  if(Img.Raster->Size1D % 16)
    IMG.width = 16*(Img.Raster->Size1D/16+1);
  else
    IMG.width = Img.Raster->Size1D;
  if(IMG.width < Img.Raster->Size1D)
      return ErrUnsupportedResolution;

  memset(&HDR,0,sizeof(HDR));
  strncpy(HDR.name,Name,sizeof(HDR.name));
  HDR.attributes=0;
  HDR.version=0;
  HDR.modify_time = HDR.create_time = time(NULL);
  HDR.archive_time=0;
  HDR.modify_number=0;
  HDR.application_info=0;
  HDR.sort_info=0;
  memcpy(HDR.type,"vIMG",4);
  memcpy(HDR.id,"View",4);
  HDR.seed=0;
  HDR.next_record=0;
  
  HDR.number_records=1;
  if(PropComment!=NULL && PropComment->DataSize>0 && PropComment->Data!=NULL)
      HDR.number_records++;

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

  if(SavePdbHeader(F,HDR)!=78) {result=ErrWriteFile; goto FINISH;}

  strncpy(IMG.name,Name,sizeof(IMG.name));
  IMG.version = 1;			// RLE Compressed
  IMG.reserved_1 = 0;
  IMG.note = 0;
  IMG.x_last = 0;
  IMG.y_last = 0;
  IMG.reserved_2 = 0;
  IMG.x_anchor = 0xFFFF;
  IMG.y_anchor = 0xFFFF;
  IMG.width = Img.Raster->Size1D;
  if(IMG.width % 16) IMG.width=(16*(IMG.width/16+1));
  IMG.height = Img.Raster->Size2D;

  {
    size_t Packets = 2 * ((Img.Raster->Size1D*Img.Raster->GetPlanes()) / 8) * Img.Raster->Size2D;
    Buffer = (uint8_t*)calloc(512,sizeof(uint8_t*));
    if(Buffer==NULL) {result=ErrNoMem;goto FINISH;}
    uint8_t *p = (uint8_t*)malloc(Packets);
    if(p==NULL) {result=ErrNoMem;goto FINISH;}

    const int bits_per_pixel = Img.Raster->GetPlanes();
    const unsigned x_increment = 8/bits_per_pixel;
    unsigned literal = 0;
    unsigned repeat = 0;

    uint8_t *q = p;
    for(unsigned y=0; y<Img.Raster->Size2D; y++)
    {
      const uint8_t *scanline = (uint8_t *)Img.Raster->GetRow(y);
      if(scanline==NULL) {result=ErrAccessData; goto FINISH_free;}
      unsigned x = 0;
      while(x < IMG.width)
      {
        if(x < Img.Raster->Size1D)
          Buffer[literal+repeat] = ~(*scanline++);
        else
          Buffer[literal+repeat] = ~(scanline[-1]);

        //printf("\nx=%u buf[%u+%u]=%u sc=%u ", x, literal, repeat, Buffer[literal+repeat], scanline[-1]);
        if(((literal+repeat)>0) && Buffer[literal+repeat]==Buffer[literal+repeat-1])
        {
          if(repeat == 0)
          {
            literal--;
            repeat++;
          }
          repeat++;
          if(0x7f < repeat)
          {
            q = EncodeRLE(q,Buffer,literal,repeat);
            literal=0;
            repeat=0;
          }
        }
        else
        {
          if(repeat <= 2)
              literal+=repeat;
          else
          {
            q = EncodeRLE(q,Buffer,literal,repeat);
            Buffer[0] = Buffer[literal+repeat];
            literal=0;
          }
          literal++;
          repeat = 0;
          if(0x7f < literal)
          {
            q = EncodeRLE(q, Buffer, 0x80, 0);
            literal -= 0x80;
            memmove(Buffer, Buffer+0x80, literal);
          }          
        }

        x += x_increment;
      }
    }

    q = EncodeRLE(q,Buffer,literal,repeat);  

    WrDWORD_HiEnd(ftell(F)+8*HDR.number_records, F);
    if(fwrite(RecHeader,1,4,F)!=4) {result=ErrWriteFile; goto FINISH_free;}
    if(HDR.number_records > 1)
    {
      WrDWORD_HiEnd(ftell(F)+8*58, F);  // +p +q ??
      if(fwrite(RecHeaderCom,1,4,F)!=4) {result=ErrWriteFile; goto FINISH;}
    }

    if(SavePdbImage(F,IMG)!=58) result=ErrWriteFile;
    else
    {
      //printf("ftell(f) %u ",ftell(F));
      fwrite(p,1,q-p,F);
      if(HDR.number_records > 1)
          fwrite(PropComment->Data,1,PropComment->DataSize,F);
    }

FINISH_free:
    free(p);
  }

FINISH:  
  fclose(F);
  if(Buffer!=NULL) free(Buffer);

return result;
}

#endif


#endif
//-------------------End of PDB routines------------------


//-----------------------PCX------------------------------
#ifdef SupportPCX

// http://fileformats.archiveteam.org/wiki/PCX

struct RGBColor
       {
	uint8_t Red,Green,Blue;
       };

struct PCXHeader{
       uint8_t Id0;
       uint8_t Version;
       uint8_t encoding;		///< 0 No encoding (rarely used); 1 Run-length encoding (RLE)
       uint8_t BitPerPix;		///< The number of bits constituting one plane. Most often 1, 2, 4 or 8.
       uint16_t x0,y0;
       uint16_t x1,y1;
       uint16_t horizontal;		///< The horizontal image resolution in DPI.
       uint16_t vertical;		///< The vertical image resolution in DPI.
       RGBColor ColorMap[16];	///< The EGA palette for 16-color images.
       uint8_t reserved;
       uint8_t NumPlanes;		///< The number of color planes constituting the pixel data. Mostly chosen to be 1, 3, or 4. 
       uint16_t BytesPerLine;	///< The number of bytes of one color plane representing a single scan line.
       uint16_t PaletteInfo;	///< The mode in which to construe the palette: 1 The palette contains monochrome or color information ; 2 The palette contains grayscale information.
//     uint16_t SrcHres;		///< The horizontal resolution of the source system's screen.
//     uint16_t SrcVres;	The vertical resolution of the source system's screen. 
       uint8_t user[58];
       };


#if SupportPCX>=4 || SupportPCX==2

inline long LoadPCXHeader(FILE *f, PCXHeader &HDR)
{
#if defined(__PackedStructures__)
 return(fread(&HDR,1,sizeof(PCXHeader),f));
#else
 return(loadstruct(f,"bbbbwwwwwwa48bbwwa58",
	 &HDR.Id0, &HDR.Version, &HDR.encoding, &HDR.BitPerPix, 
	 &HDR.x0, &HDR.y0, &HDR.x1, &HDR.y1, &HDR.horizontal, &HDR.vertical,
	 &HDR.ColorMap, &HDR.reserved, &HDR.NumPlanes, &HDR.BytesPerLine, &HDR.PaletteInfo,
	 &HDR.user));
#endif
}

Image LoadPicturePCX(const char *Name)
{
const int Mask=0xC0;
FILE *f;
uint16_t X,Y;
uint8_t C,Cnt;
PCXHeader Header;
uint8_t *data;
int Aplane;
Raster1DAbstract *pbuf=NULL;
APalette *Palette=NULL;
Raster2DAbstract *Raster=NULL;
Image Img;
long ldblk;

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

  if(LoadPCXHeader(f,Header)!=128)
      {fclose(f);return(Img);}
  if(Header.Id0!=10) goto ENDPROC;	/*incorrect identifier*/
  if(Header.encoding>1) goto ENDPROC;	// Unknown encoding.

  if(Header.BitPerPix==8 && Header.NumPlanes==3)
      Raster = CreateRaster2DRGB(Header.x1-Header.x0+1,Header.y1-Header.y0+1, Header.BitPerPix);
  else if(Header.BitPerPix==8 && Header.NumPlanes==4)
      Raster = CreateRaster2DRGBA(Header.x1-Header.x0+1,Header.y1-Header.y0+1, Header.BitPerPix);
  else
      Raster = CreateRaster2D(Header.x1-Header.x0+1,Header.y1-Header.y0+1,
			  Header.BitPerPix*Header.NumPlanes);
  if(Raster==NULL) goto ENDPROC;

//  p.typ='G';
// if(p.planes=24) p.typ='C';
//  if(AlineProc!=NULL) AlineProc->InitPassing(Raster->Size2D,'Loading PCX');

  Aplane = Header.NumPlanes;

  ldblk = (Raster->GetPlanes()*Raster->GetSize1D()+7)/8;	// Calculate line block size for only one plane.
  if(Header.NumPlanes!=1)
  {
    pbuf = CreateRaster1D(Raster->GetSize1D(),Header.BitPerPix);
    data = (uint8_t *)pbuf->Data1D;
    if(data==NULL)
    {
      delete Raster;
      Raster=NULL;
      goto ENDPROC;
    }
    ldblk = (pbuf->GetPlanes()*pbuf->GetSize1D()+7)/8;	// Calculate line block size for only one plane.
  }

  Y=0;
  Cnt=0;
  while(Y<Raster->Size2D)
    {
    Aplane=Header.NumPlanes;
    if(Header.NumPlanes==1) data=(uint8_t *)Raster->GetRow(Y);

    do {

       if(Header.encoding==0)
       {
         X = fread(data,1,ldblk,f);
         while(X<Header.BytesPerLine) 
         {
           fgetc(f); X++;
         }
       }
       else		// RLE encoding
       {
         uint8_t *adata = data;
         X = 0;
         if(Cnt>0) goto RestOfLast;
         while(X<Header.BytesPerLine)
	    {
	    if(fread(&C,1,1,f) != 1) break;
	    Cnt=1;
	    if((C & Mask)==Mask)	/*the prefix was added*/
		  {
		  Cnt=C & 0x3F;
		  fread(&C,1,1,f);
		  }
RestOfLast:
	    while(Cnt>0)
	       {
	       if(X>=Header.BytesPerLine) break;  /*repeater overflows over a line*/
	       if(X<ldblk)
	         *adata++ = C;		/* some PCX image have longer rows with garbage data */
	       X++;
	       Cnt--;
	       }
	    }
	}
       if(X!=Header.BytesPerLine)
		{		/*load error*/
		delete Raster;
		Raster = NULL;
		goto ENDPROC;
		}

					/*adding a next bit plane*/
       if(Header.NumPlanes!=1)
	 {
	 if(Header.BitPerPix==1)
	    {
	    Raster->GetRowRaster(Y)->Join1Bit(data,Raster->GetPlanes()-Aplane);
	    }
	 if((Header.BitPerPix==8)&&(Raster->GetPlanes()==24 || Raster->GetPlanes()==32))
	    {		/*RGB image*/
//	    if not aplane in [1,2,3]  asm int 3; }
	    Raster->GetRowRaster(Y)->Join8Bit(data,Raster->GetPlanes()-8*Aplane);
	    }
	  }

      Aplane--;		/*loop for bit rows*/
      } while(Aplane>=1);

//  if(AlineProc!=NULL) AlineProc->NextLine();
  Y++;
  if(Cnt>0) 	//repeater overflows over line}
	{		/*load error*/
	delete Raster;
	Raster=NULL;
	goto ENDPROC;
	}
   }

 //printf("ftell(f)=%Xh", ftell(f));

 if(Header.BitPerPix==8)   //loading of palette
     {
     C=0;
     fread(&C,1,1,f);
     if (C!=12) goto ENDPROC;
     Palette=BuildPalette(256,8);
     if(Palette!=NULL)
	fread(Palette->Data1D,(7+Palette->GetSize1D()*Palette->GetPlanes())/8,1,f);
     if(GrayPalette(Palette,Raster->GetPlanes()))
		{
		delete Palette;
		Palette=NULL;
		}
     }
  else if (Raster->GetPlanes()==4 || Raster->GetPlanes()==2)  /*move palette from header*/
	{
	Palette=BuildPalette(1<<Raster->GetPlanes(),8);
	if(Palette!=NULL)
	  memcpy(Palette->Data1D,Header.ColorMap,(7+Palette->Size1D*Palette->GetPlanes())/8);
	if(GrayPalette(Palette,Raster->GetPlanes()))
		{
		delete Palette;
		Palette=NULL;
		}
	}

ENDPROC:
  if(pbuf) delete pbuf;
  fclose(f);
  Img.AttachRaster(Raster);
  Img.AttachPalette(Palette);
  return(Img);
}  //LoadPCX
#endif


#if SupportPCX>=3

inline long SavePcxHeader(FILE *f, const PCXHeader &HDR)
{
#if defined(__PackedStructures__)
  return(fwrite(&HDR,1,sizeof(HDR),f));
#else
 return(savestruct(f,"bbbbwwwwwwa48bbwwa58",
	 HDR.Id0, HDR.Version, HDR.encoding, HDR.BitPerPix, 
	 HDR.x0, HDR.y0, HDR.x1, HDR.y1, HDR.horizontal, HDR.vertical,
	 HDR.ColorMap, HDR.reserved, HDR.NumPlanes, HDR.BytesPerLine, HDR.PaletteInfo,
	 HDR.user));
#endif
}

int SavePicturePCX(const char *Name,const Image &Img)
{
const int Mask=0xC0;
const int MaxCnt=63;
FILE *F;
PCXHeader PCXFile;
uint8_t *data;
const uint8_t *ptrb;
uint8_t *bbuf;
APalette *Palette=NULL;
int k;
int result = 0;
uint16_t datapos;
uint8_t C, OC;
uint16_t X,Y,Cnt;
uint8_t Aplane;

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

  bbuf = NULL;

  memset(&PCXFile,0,sizeof(PCXFile));
  PCXFile.Id0 = 10;
  PCXFile.Version = 5;
  PCXFile.encoding = 1;
  PCXFile.BitPerPix = 1;
  PCXFile.x0 = 0;
  PCXFile.y0 = 0;
  PCXFile.x1 = Img.Raster->GetSize1D()-1;
  PCXFile.y1 = Img.Raster->Size2D-1;
  PCXFile.NumPlanes = Img.Raster->GetPlanes();
  PCXFile.BytesPerLine = PCXFile.x1-PCXFile.x0+1;
  PCXFile.PaletteInfo = 1;		// The palette contains monochrome or color information.

  switch(Img.Raster->GetPlanes())
   {
   case 1:if(Img.Palette!=NULL && Img.Palette->Size1D>=2)
          {
            PCXFile.NumPlanes = 2;		// Palette cannot be displayed in 1 plane bilevel mode, add a dummy plane.
            C = 2;
            goto InternalPalette;
          }
          PCXFile.BytesPerLine = (PCXFile.BytesPerLine+7) >> 3;
	  break;
   case 2:C = 4;
	  goto InternalPalette;
   case 4:C = 16;
InternalPalette:
	  bbuf = (uint8_t *)malloc((Img.Raster->GetSize1D()+7) >> 3);
	  PCXFile.BytesPerLine=(PCXFile.BytesPerLine+7) >> 3;
	  Palette = Img.Palette;
	  if(Palette==NULL)
          {
            Palette = BuildPalette(1<<Img.Raster->GetPlanes(),8);
            PCXFile.PaletteInfo = 2;
          }
          k = Palette->GetPlanes()/3 - 8;
          if(k<0) k = 0;
	  while(C-->0)
          {
	    PCXFile.ColorMap[C].Red = Palette->R(C) >> k;
            PCXFile.ColorMap[C].Green = Palette->G(C) >> k;
            PCXFile.ColorMap[C].Blue = Palette->B(C) >> k;
          }
	  if(Palette!=Img.Palette) delete Palette;
	  Palette=NULL;
	  break;
   case 8:PCXFile.BitPerPix = 8;
	  PCXFile.NumPlanes = 1;
	  break;
   case 24:PCXFile.BitPerPix = 8;		// RGB format
	  PCXFile.NumPlanes = 3;
	  bbuf = (uint8_t *)malloc(Img.Raster->GetSize1D());
	  break;
   case 64:
   case 32:PCXFile.BitPerPix = 8;		// RGBA format
	  PCXFile.NumPlanes = 4;
	  bbuf = (uint8_t *)malloc(Img.Raster->GetSize1D());
	  break;
   default:return(-2);
  }
  if(PCXFile.NumPlanes!=1 && bbuf==NULL)
    return -4;

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

  if(SavePcxHeader(F,PCXFile)!=128) {result=ErrWriteFile; goto FINISH;}

  data = (uint8_t *)malloc(2*PCXFile.BytesPerLine);

  for(Y=0; Y<Img.Raster->Size2D; Y++)
    {
    for(Aplane=0;Aplane<PCXFile.NumPlanes;Aplane++)
	{
	datapos = 0;
	Cnt = 0;
	ptrb = (const uint8_t *)Img.Raster->GetRow(Y);

	if(PCXFile.NumPlanes!=1)
	   {
	   if(PCXFile.BitPerPix==1)
	     Img.Raster->GetRowRaster(Y)->Peel1Bit(bbuf,Aplane);
	   if(PCXFile.BitPerPix==8)
	     Img.Raster->GetRowRaster(Y)->Peel8Bit(bbuf,8*Aplane);
	   ptrb = bbuf;
	   }

	for(X=0;X<PCXFile.BytesPerLine;X++)
	  {
	  C = *ptrb;
	  ptrb++;

	  if((Cnt!=0) && ((OC!=C)||(Cnt==MaxCnt)) )
	    {
	    if( (Cnt!=1) || ((OC & Mask)==Mask) ) //add a prefix
		{
		data[datapos]=Cnt | Mask;
		datapos++;
		}
	    data[datapos]=OC;
	    datapos++;
	    Cnt=0;
	    }
	  OC=C;
	  Cnt++;
	  }

       if(Cnt!=0) 	//end of line
	   {
	   if( (Cnt!=1) || ((OC & Mask)==Mask) ) //Add prefix
		{
		data[datapos]=Cnt | Mask;
		datapos++;
		}
	   data[datapos]=OC;
	   datapos++;
	   Cnt = 0;
	   }
       if(fwrite(data,datapos,1,F)!=1)
           {result=ErrWriteFile; goto FINISH;}
       }
//  if AlineProc<>nil then AlineProc^.NextLine;
    }

  if(data) {free(data);data=NULL;}

  if(Img.Raster->GetPlanes()==8)
    {
    Palette = Img.Palette;
    if(Palette==NULL)
	{
	Palette = BuildPalette(256,8);
	if(Palette==NULL)		// Error creating palette
	  {
	  //printf("Memory exhausted.");
          result = ErrNoMem;
	  goto FINISH;
	  }
	FillGray(Palette,8);
	}

    fputc(12,F);	// palette

    k = Palette->GetPlanes()/3 - 8;
    if(k<0) k = 0;
    for(X=0; X<=255; X++)
    {
      fputc(Palette->R(X)>>k,F);	//Red;
      fputc(Palette->G(X)>>k,F);	//Green;
      fputc(Palette->B(X)>>k,F);	//Blue;
    }

    if(Palette!=Img.Palette) delete Palette;
    Palette = NULL;
  }

FINISH:
  if(bbuf!=NULL) {free(bbuf);bbuf=NULL;}
  fclose(F);
return(result);
}
#endif

#endif
//-------------------End of PCX routines------------------


//--------------------------QOI----------------------------
#ifdef SupportQOI


struct QOIHeader
{
  char		magick[4];
  uint32_t	columns;
  uint32_t	rows;
  uint8_t	channels;
  uint8_t	colorspace;
};


typedef union
{
  struct { uint8_t R, G, B, A; } rgba;
  uint32_t v;
} qoi_rgba_t;


static const char QOI_MAGICK[] = "qoif";

#define QOI_SRGB   0
#define QOI_LINEAR 1

#define QOI_OP_INDEX  0x00	/* 00xxxxxx */
#define QOI_OP_DIFF   0x40	/* 01xxxxxx */
#define QOI_OP_LUMA   0x80	/* 10xxxxxx */
#define QOI_OP_RUN    0xc0	/* 11xxxxxx */
#define QOI_OP_RGB    0xfe	/* 11111110 */
#define QOI_OP_RGBA   0xff	/* 11111111 */

#define QOI_MASK_2    0xc0	/* 11000000 */

#define QOI_COLOR_HASH(C) (C.rgba.R*3 + C.rgba.G*5 + C.rgba.B*7 + C.rgba.A*11)


#if SupportQOI>=4 || SupportQOI==2


inline long LoadQoiHeader(FILE *f, QOIHeader &HDR)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(SU),f));
#else
 return(loadstruct(f,"a4DDbb",
	 &HDR.magick, &HDR.columns, &HDR.rows,
	 &HDR.channels, &HDR.colorspace));
#endif
}


Image LoadPictureQOI(const char *Name)
{
FILE *f;
QOIHeader HDR;
Raster2DAbstract *Raster=NULL;
Raster1DAbstract *pRow;
Image Img;
unsigned x, y;
RGBQuad ColorRGB;
qoi_rgba_t lut[64];
qoi_rgba_t px;
int8_t vg;
uint8_t run, b;

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

 if(LoadQoiHeader(f,HDR)!=14) goto Finish;
 if(memcmp(HDR.magick,QOI_MAGICK,4)) goto Finish;

 if(HDR.rows==0 || HDR.columns==0 || HDR.rows>=65536 || HDR.columns>=65536)
   goto Finish;
 if(HDR.colorspace!=QOI_SRGB && HDR.colorspace!=QOI_LINEAR)
   goto Finish;

 switch(HDR.channels)
 {
   case 3: Raster = CreateRaster2DRGB(HDR.columns,HDR.rows,8);
           break;
   case 4: Raster = CreateRaster2DRGBA(HDR.columns,HDR.rows,8);
           break;
 }
 if(Raster==NULL) goto Finish;

 px.rgba.R = 0;
 px.rgba.G = 0;
 px.rgba.B = 0;
 px.rgba.A = 255;
 memset(&lut,0,sizeof(lut));
 memset(&ColorRGB,0,sizeof(ColorRGB));
 x = y = 0;
 pRow = Raster->GetRowRaster(0);
 if(pRow!=NULL)
   while(!feof(f))
   {
     run = 0;
     b = fgetc(f);
     if(b == QOI_OP_RGB)
     {
       if(fread(HDR.magick,1,3,f)!=3) break;
       px.rgba.R = HDR.magick[0];
       px.rgba.G = HDR.magick[1];
       px.rgba.B = HDR.magick[2];
     }
     else if(b == QOI_OP_RGBA)
     {
       if(fread(HDR.magick,1,4,f)!=4) break;
       px.rgba.R = HDR.magick[0];
       px.rgba.G = HDR.magick[1];
       px.rgba.B = HDR.magick[2];
       px.rgba.A = HDR.magick[3];
     }
     else switch(b & QOI_MASK_2)
     {
       case QOI_OP_INDEX: px = lut[b & ~QOI_MASK_2];
  			break;
       case QOI_OP_DIFF:  px.rgba.R += ((b >> 4) & 0x03) - 2;
                        px.rgba.G += ((b >> 2) & 0x03) - 2;
                        px.rgba.B += ( b       & 0x03) - 2;
			break;
       case QOI_OP_LUMA:  vg = (b & ~QOI_MASK_2) - 32;
                        b = fgetc(f);
			//printf("pos:%X ",ftell(f));
			px.rgba.R += vg - 8 + ((b >> 4) & 0x0f);
                        px.rgba.G += vg;
                        px.rgba.B += vg - 8 +  (b       & 0x0f);
			break;
       case QOI_OP_RUN:   run = b & ~QOI_MASK_2;
			break;
     }
     lut[QOI_COLOR_HASH(px) % 64] = px;   

     ColorRGB.R = px.rgba.R;
     ColorRGB.G = px.rgba.G;
     ColorRGB.B = px.rgba.B;
     ColorRGB.O = ~px.rgba.A;
     do
     {
       pRow->Set(x,&ColorRGB);
       if(++x>=Raster->Size1D)
       {
         x = 0;
         if(++y>=Raster->Size2D) goto Finish;	// All data are read.
         pRow = Raster->GetRowRaster(y);
         if(pRow==NULL) goto ReadProblem;
       }
     } while(run-- > 0);
   } 

ReadProblem:	// We should not get here, only when problem with file occurs
 while(y<Raster->Size2D)
 {
   Raster->Set(x,y,&ColorRGB);	// Feed all remaining values.
   if(++x>=Raster->Size1D)
   {
     x = 0;
     ++y;
   }
 }

Finish:
 if(f) {fclose(f);f=NULL;}
 Img.AttachRaster(Raster); 
 return(Img);
}	/*LoadQOI*/

#endif


#if SupportQOI>=3

inline long SaveQoiHeader(FILE *f, const QOIHeader &HDR)
{
#if defined(__PackedStructures__)
 return(fwrite(&HDR,1,sizeof(HDR),f));
#else
 return(savestruct(f,"a4DDbb",
	 HDR.magick, HDR.columns, HDR.rows,
	 HDR.channels, HDR.colorspace));
#endif
}

int SavePictureQOI(const char *Name,const Image &Img)
{
FILE *f;
QOIHeader HDR;
Raster1DAbstract *pRow;
qoi_rgba_t px, pp;
qoi_rgba_t lut[64];
RGBQuad ColorRGB;
unsigned x, y;
uint8_t run;

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

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

 memcpy(HDR.magick,QOI_MAGICK,4);
 HDR.rows = Img.Raster->Size2D;
 HDR.columns = Img.Raster->Size1D;
 HDR.channels = 3;
 if(Img.Raster->Channels()==4) HDR.channels=4;
 HDR.colorspace = QOI_SRGB;
 if(SaveQoiHeader(f,HDR)!=14) 
 {
   fclose(f);
   return ErrWriteFile;
 }

 px.rgba.R = 0;
 px.rgba.G = 0;
 px.rgba.B = 0;
 px.rgba.A = 255;
 run = 0;
 memset(&lut,0,sizeof(lut));
 x = y = 0;
 pRow = Img.Raster->GetRowRaster(y); 
 while(pRow)
 {
   pp = px;
   pRow->Get(x,&ColorRGB);
   if(++x >= HDR.columns)
   {
     x = 0;
     y++;
     pRow = Img.Raster->GetRowRaster(y);
   }
   px.rgba.R = ColorRGB.R;
   px.rgba.G = ColorRGB.G;
   px.rgba.B = ColorRGB.B;
   px.rgba.A = ~ColorRGB.O;

   if(pp.v == px.v)
   {
     if(++run == 62)
     {
       fputc(QOI_OP_RUN|(run-1),f);
       run = 0;
     }
     continue;
   }
   if(run > 0)
   {
     fputc(QOI_OP_RUN|(run-1),f);
     run = 0;
   }
   const uint8_t idx = QOI_COLOR_HASH(px) % 64;
   if(lut[idx].v == px.v)
   {
     fputc(QOI_OP_INDEX | idx,f);
     continue;
   }
   lut[QOI_COLOR_HASH(px) % 64] = px;

   if(pp.rgba.A == px.rgba.A)
   {
     const int16_t vr = (int16_t)px.rgba.R - (int16_t)pp.rgba.R;
     const int16_t vg = (int16_t)px.rgba.G - (int16_t)pp.rgba.G;
     const int16_t vb = (int16_t)px.rgba.B - (int16_t)pp.rgba.B;     

   if((vr>-3) && (vr<2) && (vg>-3) && (vg<2) && (vb>-3) && (vb<2))
   {
     const uint8_t diff = (uint8_t)((vr + 2) << 4 | (vg + 2) << 2 | (vb + 2));
     fputc(QOI_OP_DIFF | diff, f);
   }
   else 
   {
     const int16_t vg_r = vr - vg;
     const int16_t vg_b = vb - vg;
     if((vg_r>-9) && (vg_r<8) && (vg>-33) && (vg<32) && (vg_b>-9) && (vg_b<8))
     {
       HDR.magick[0] = QOI_OP_LUMA | (uint8_t)(vg + 32);
       HDR.magick[1] = (uint8_t)((vg_r + 8) << 4 | (vg_b +  8));
       fwrite(HDR.magick,1,2,f);         
     }
     else
     {
       fputc(QOI_OP_RGB,f);
       fputc(px.rgba.R,f);
       fputc(px.rgba.G,f);
       fputc(px.rgba.B,f);
     }
   }
  }
  else
  {
    fputc(QOI_OP_RGBA,f);
    fputc(px.rgba.R,f);
    fputc(px.rgba.G,f);
    fputc(px.rgba.B,f);
    fputc(px.rgba.A,f);
  }
 }

 if(run > 0)
 {
   fputc(QOI_OP_RUN|(run-1),f);
 }

	// Write the QOI end marker: seven 0x00 bytes followed by 0x01.
 for(x=0; x<7; x++)
   fputc(0x00,f);
 fputc(0x01,f);
 fclose(f);

return 0;
}

#endif

#endif
//-------------------End of QOI routines------------------



//-------------------------RAS-TopoL-----------------------
#ifdef SupportRAS

struct TopolRasHeader
	{
	char Name[20];
	int16_t Rows;
	int16_t Cols;
	int16_t TypSou; // 0-binarni, 1-8 bitu,2-8 bitu+PAL,3-4 bity,
		      // 4-4 bity+PAL, 5-24 bite,6-16 bite,7- 32 bite}
	int32_t Zoom;
	int16_t Verze;
	int16_t Komprese;	// {0 - nekomprimovano}	 {od Verze 1}
	int16_t Stav;
	double xRasMin;
	double yRasMin;
	double xRasMax;
	double yRasMax;
	double Scale;	//from release 2
	uint16_t TileWidth;	      // tilt width in pixels
	uint16_t TileHeight;      // tile height in pixels
	int32_t TileOffsets;   // offset na pole longintu, ktere jsou adresami tilu v rastru (adresy se pocitaji od 0)}
	int32_t TileByteCounts;/* offset na pole wordu, ktere obsahuji pocet bytu ulozenych v
				 v jednotlivych tilech velikost tilu se muze lisit v zavislosti
				 na hodnote TileCompression*/
	uint8_t TileCompression; //0 - nekomprimovano, 1 - varianta TIFF Packbits, 2 - CCITT G3

	uint8_t Dummy[423];
	};

struct paletteRAS
   {
   uint8_t Flag;
   uint8_t Red;
   uint8_t Green;
   uint8_t Blue;
   };


#if SupportRAS>=4 || SupportRAS==2

inline long LoadTopolHeader(FILE *f, TopolRasHeader &SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(SU),f));
#else
 return(loadstruct(f,"a20wwwdwwweeeeewwddba423",
	 &SU.Name,&SU.Rows,&SU.Cols,&SU.TypSou,&SU.Zoom,&SU.Verze,
         &SU.Komprese,&SU.Stav,
         &SU.xRasMin,&SU.yRasMin,&SU.xRasMax,&SU.yRasMax,&SU.Scale,	//from release 2
	 &SU.TileWidth,&SU.TileHeight,&SU.TileOffsets,&SU.TileByteCounts,
         &SU.TileCompression,&SU.Dummy));
#endif
}

Image LoadPictureRAS(const char *Name)
{
FILE *f;
uint16_t Ldblk;
uint16_t j,i,k;
TopolRasHeader Header;
Image Img;
Raster2DAbstract *Raster=NULL;
APalette *Palette=NULL;
char *NameBuffer=NULL;
uint32_t *Offsets=NULL;
uint16_t TilesAcross, TilesDown;
uint16_t TilX, TilY;

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

  memset(&Header,0,sizeof(Header));
  LoadTopolHeader(f,Header);

  for(i=0;i<sizeof(Header.Name);i++)
    {
    if(Header.Name[i]<' ') goto FINISH;
    }
  if(Header.Komprese!=0) goto FINISH;
  if(Header.Rows<=0 || Header.Cols<=0 ) goto FINISH;
  if(Header.Verze>2)  goto FINISH;		//unknown version

  switch(Header.TypSou)
    {
    case 0: i=1;  break;
    case 1:
    case 2: i=8;  break;
    case 3:
    case 4: i=4;  break;
    case 5: i=24; break;  //if( Header.TypSou = 5 p.typ='C';	{True Color}
    case 6: i=16; break;
    case 7: i=32; break;
    default: goto FINISH;
    }
  Raster = CreateRaster2D(Header.Cols,Header.Rows,i);
  if(Raster==NULL) goto FINISH;
  // if(AlineProc!=NULL) AlineProc^.InitPassing(p.y,'Loading RAS');

  switch(Header.Verze)
   {
   case 0:
   case 1:fseek(f,0x200,SEEK_SET);
	  Ldblk=((long)Raster->GetPlanes()*Header.Cols+7) / 8;

	  for(i=0;i<Header.Rows;i++)
	     {
	     if(fread(Raster->GetRow(i),Ldblk,1,f)!=1) goto FINISH;
	     //if( AlineProc!=NULL  AlineProc^.NextLine;
	     }

	 break;

   case 2:
       TilesAcross = (Header.Cols+Header.TileWidth-1) / Header.TileWidth;
       TilesDown   = (Header.Rows+Header.TileHeight-1) / Header.TileHeight;

       if(Header.TileCompression!=0)
		{
TileFailed:	delete Raster; Raster=NULL;
		goto FINISH;
		}
       //dlazdice.create(Header.TileWidth,Header.TileHeight,p.Planes);
       Offsets=(uint32_t *)malloc(TilesAcross*TilesDown*sizeof(uint32_t));
       if(Offsets==NULL) goto TileFailed;

       fseek(f,Header.TileOffsets,SEEK_SET);
       fread(Offsets,TilesAcross*TilesDown,sizeof(uint32_t),f);

       for(TilY=0;TilY<Header.Rows;TilY+=Header.TileHeight)
	 for(TilX=0;TilX<TilesAcross;TilX++)
	   {
	   Ldblk = Raster->GetSize1D() - TilX*Header.TileWidth;
	   if(Ldblk>Header.TileWidth) Ldblk=Header.TileWidth;
	   long SkipBlk = ((long)Raster->GetPlanes() * (Header.TileWidth-Ldblk)+7) / 8;
	   Ldblk = ((long)Raster->GetPlanes()*Ldblk+7) / 8;

	   fseek(f,Offsets[(TilY/Header.TileHeight)*TilesAcross+TilX],SEEK_SET);
	   j = TilX * (Ldblk+SkipBlk);
	   for(i=0;i<Header.TileHeight;i++)
		{
		uint8_t *Data = (uint8_t *)Raster->GetRow(i+TilY);
		if(Data)
		   {
		   if(fread(Data+j,Ldblk,1,f)!=1) goto FINISH;
		   if(SkipBlk>0) fseek(f,SkipBlk,SEEK_CUR);
		   }
		    //if(AlineProc!=NULL)  AlineProc^.NextLine;
		}
	    }

       free(Offsets);
       Offsets = NULL;
       break;

    }

 fclose(f); f=NULL;

 if(Raster!=NULL && (Header.TypSou==2 || Header.TypSou==4)) //the palette should be loaded
	{
	j=i=strlen(Name);
	NameBuffer=(char *)malloc(i+5);
	strcpy(NameBuffer,Name);

	while(--i>0)
	  {
	  if(Name[i]=='.') break;
	  if(Name[i]=='/' || Name[i]=='\\' || Name[i]==':')
	       {i=j;break;}
	  }
	strcpy(NameBuffer+i,".PAL");
	if((f=fopen(NameBuffer,"rb"))==NULL)
	     {
	     strcpy(NameBuffer+i,".pal");
	     if((f=fopen(NameBuffer,"rb"))==NULL)
		 {
		 NameBuffer[i]=0;
		 if((f=fopen(NameBuffer,"rb"))==NULL)
			goto PalSkip;
		 }
	     }

          {
          int tmp = fgetc(f);		//startup info
          if(tmp==EOF) goto PalFail;
          Ldblk = tmp + 1;
          }

	Palette=BuildPalette(1 << Raster->GetPlanes(),8);

	i=1<<Raster->GetPlanes();
	if(i<Ldblk) Ldblk=i;
	for(i=0; i<Ldblk; i++)
	  {
          int tmp = fgetc(f);
	  if(tmp ==EOF) goto PalFail;
	  Palette->setR(tmp,fgetc(f));	//Red
	  Palette->setG(tmp,fgetc(f));	//Green
	  Palette->setB(tmp,fgetc(f));	//Blue
	  }
PalFail:
	if(f) {fclose(f);f=NULL;}
	if(GrayPalette(Palette,Raster->GetPlanes()))
		{
		delete Palette;
		Palette=NULL;
		}
	}
PalSkip:

			//the mez file should be loaded now
  if(Raster!=NULL && Header.TypSou>=1 && Header.TypSou<=4)
	{
	j=i=strlen(Name);
	if(NameBuffer==NULL) NameBuffer=(char *)malloc(i+5);
	strcpy(NameBuffer,Name);

	while(--i>0)
	  {
	  if(Name[i]=='.') break;
	  if(Name[i]=='/' || Name[i]=='\\' || Name[i]==':')
	       {i=j;break;}
	  }
	strcpy(NameBuffer+i,".MEZ");
	if((f=fopen(NameBuffer,"rb"))==NULL)
	     {
	     strcpy(NameBuffer+i,".mez");
	     if((f=fopen(NameBuffer,"rb"))==NULL)
		 goto MezFail;
	     }

	uint8_t Mez[256];
	Ldblk = fread(Mez,1,sizeof(Mez),f);
	if(Ldblk==0) goto MezFail;

	for(i=0;i<Raster->Size2D;i++)
	  for(j=0;j<Raster->GetSize1D();j++)
	    {
	    k=Raster->GetValue2D(j,i);
	    if(k<Ldblk) Raster->SetValue2D(j,i,Mez[k]);
	    }
	//Operation1(p,ReTabB,addr(Mez),nil);
	}
MezFail:

FINISH:
  if(f) fclose(f);
  if(NameBuffer) {free(NameBuffer);NameBuffer=NULL;}
  Img.AttachRaster(Raster);
  Img.AttachPalette(Palette);
return(Img);
}
#endif


#if SupportRAS>=3

inline long SaveTopolHeader(FILE *f, const TopolRasHeader &SU)
{
#if defined(__PackedStructures__)
 return(fwrite(&SU,1,sizeof(SU),f));
#else
 return(savestruct(f,"a20wwwdwwweeeeewwddba423",
	 SU.Name,SU.Rows,SU.Cols,SU.TypSou,SU.Zoom,SU.Verze,
         SU.Komprese,SU.Stav,
         SU.xRasMin,SU.yRasMin,SU.xRasMax,SU.yRasMax,SU.Scale,	//from release 2
	 SU.TileWidth,SU.TileHeight,SU.TileOffsets,SU.TileByteCounts,
         SU.TileCompression,SU.Dummy));
#endif
}

int SavePictureRAS(const char *Name,const Image &Img)
{
FILE *f;
uint16_t Ldblk;
uint16_t i;
TopolRasHeader Header;
paletteRAS pal;
char *PalName=NULL;

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

  memset(&Header,0,sizeof(Header));
  memset(&Header.Name,' ',sizeof(Header.Name));
  Header.Cols = Img.Raster->GetSize1D();
  Header.Rows = Img.Raster->Size2D;
  switch(Img.Raster->GetPlanes())
	{
	case 1:Header.TypSou=0;
	       break;
	case 4:if(Img.Palette==NULL) Header.TypSou=3;
				else Header.TypSou=4;
	       break;
	case 8:if(Img.Palette==NULL) Header.TypSou=1;
				else Header.TypSou=2;
	       break;
	case 16:Header.TypSou=6;
		break;
	case 24:Header.TypSou=5;
		break;
	default:return(-2);
	}

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

  i = SaveTopolHeader(f,Header);
  if(i!=512) {fclose(f);return -3;}

  Ldblk = ((long)Img.Raster->GetPlanes()*Img.Raster->GetSize1D()+7) / 8;
  for(i=0;i<Header.Rows;i++)
	{
	fwrite(Img.Raster->GetRow(i),Ldblk,1,f);
	// if AlineProc<>nil then AlineProc^.NextLine;
	}

  fclose(f);

  if( Img.Palette!=NULL && (Header.TypSou==2 || Header.TypSou==4)) //now remains storing of palette
	{
	i=strlen(Name);
	PalName=(char *)malloc(i+4);

	memcpy(PalName,Name,i+1);
	memcpy(PalName+i-3,"PAL",3);

	if((f=fopen(PalName,"wb"))==NULL)
		{
		free(PalName);
		return(-1);
		}
	free(PalName);

	i = (1 << Img.Raster->GetPlanes())-1;
	fputc(i,f);

	for(i=0; i<(1 << Img.Raster->GetPlanes()); i++)
		{
		pal.Flag=i;
		pal.Red=  Img.Palette->R(i);	//Red;
		pal.Green=Img.Palette->G(i);	//Green;
		pal.Blue= Img.Palette->B(i);	//Blue;

		if(fwrite(&pal,sizeof(pal),1,f)!=1) goto PalFail;
		}
PalFail:fclose(f);
	}

return(0);
}
#endif

#endif
//-----------------End of RAS-TopoL routines----------------


//-------------------------RAS-SUN-------------------------
#ifdef SupportRAS_SUN

// https://www.fileformat.info/format/sunraster/egff.htm

// Raster type:
//0000h 	Old
//0001h 	Standard
//0002h 	Byte-encoded
//0003h 	RGB format
//0004h 	TIFF format
//0005h 	IFF format
//FFFFh 	Experimental

// Color map type:
//0000h 	No color map
//0001h 	RGB color map
//0002h 	Raw color map

struct SUNRASHeader
	{
	uint32_t ras_magic;	///< Magic (identification) number.
	uint32_t ras_width;	///< Width of image in pixels.
	uint32_t ras_height;	///< Height of image in pixels.
	uint32_t ras_depth;	///< Number of bits per pixel.
	uint32_t ras_length;	///< Size of image data in bytes.
	uint32_t ras_type;		///< Type of raster file
	uint32_t ras_maptype;
	uint32_t ras_maplength;
	};


#if SupportRAS_SUN>=4 || SupportRAS_SUN==2

inline long LoadSUNRASHeader(FILE *f,SUNRASHeader &SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(SU),f));
#else
 return(loadstruct(f,"DDDDDDDD",
	 &SU.ras_magic,&SU.ras_width,&SU.ras_height,
	 &SU.ras_depth,&SU.ras_length,&SU.ras_type,
	 &SU.ras_maptype,&SU.ras_maplength));
#endif
}

Image LoadPictureRAS_SUN(const char *Name)
{
FILE *f;
uint16_t w,i;
SUNRASHeader Header;
Image Img;
Raster2DAbstract *Raster=NULL;
APalette *Palette=NULL;

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

  memset(&Header,0,sizeof(Header));
  LoadSUNRASHeader(f,Header);

  if(Header.ras_magic != 0x59A66A95)
		 {
		 //LoadPictureSUNRAS=ErrAnother;
		 goto FINISH;		//incorrect identifier
		 }

  if(Header.ras_depth==24 && Header.ras_type!=3)
    Raster = CreateRaster2DRGB(Header.ras_width,Header.ras_height,Header.ras_depth/3);
  else
    Raster = CreateRaster2D(Header.ras_width,Header.ras_height,Header.ras_depth);
  if(Raster==NULL) goto FINISH;
  //if(AlineProc!=NULL) AlineProc^.InitPassing(p.y,'Loading SUNRAS');

  if(Header.ras_maptype!=0 && Header.ras_maplength!=0)
	 {
	 Palette = BuildPalette(1<<Header.ras_depth,8);
	 w = (Header.ras_maplength / 3) - 1;
	 if(w < (1<<Header.ras_depth) )
	    {
	    for(i=0;i<=w;i++) Palette->setR(i,fgetc(f));	//Red
	    for(i=0;i<=w;i++) Palette->setG(i,fgetc(f));	//Green
	    for(i=0;i<=w;i++) Palette->setB(i,fgetc(f));	//Blue
	    }
	 if(GrayPalette(Palette,Raster->GetPlanes()))
	    {
	    delete Palette;
	    Palette=NULL;
	    }

	 fseek(f,sizeof(Header)+Header.ras_maplength,SEEK_SET);
	 }

  {
    const long Ldblk = ((long)Header.ras_depth*Header.ras_width+7) / 8;
    const char Padding = (-Ldblk) & 1;
    for(i=0; i<Header.ras_height; i++)
    {
     if(fread(Raster->GetRow(i),Ldblk,1,f)!=1) goto FINISH;
     if(Header.ras_depth==24 && Header.ras_type!=3)
         RGB_BGR((char *)Raster->GetRow(i),Raster->GetSize1D());

     if(i<Header.ras_height-1 && Padding>0)
         fseek(f,Padding,SEEK_CUR);	//docteni do konce radku
	 //if InOutRes!=0 then goto KONEC;
	 //if AlineProc!=NULL then AlineProc^.NextLine;
    }
  }

FINISH:
  fclose(f);
  Img.AttachRaster(Raster);
  Img.AttachPalette(Palette);
  return(Img);
}
#endif


#if SupportRAS_SUN>=3


inline long SaveSUNRASHeader(FILE *f, const SUNRASHeader &SU)
{
#if defined(__PackedStructures__)
 return(fwrite(&SU,1,sizeof(SU),f));
#else
 return(savestruct(f,"DDDDDDDD",
	 SU.ras_magic, SU.ras_width, SU.ras_height,
	 SU.ras_depth, SU.ras_length, SU.ras_type,
	 SU.ras_maptype, SU.ras_maplength));
#endif
}

int SavePictureRAS_SUN(const char *Name, const Image &Img)
{
SUNRASHeader Header;
FILE *f;
uint32_t Ldblk;
char padding;
unsigned i;
int Result = 0;

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

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

  Header.ras_magic = 0x59A66A95;
  Header.ras_width = Img.Raster->Size1D;
  Header.ras_height = Img.Raster->Size2D;
  Header.ras_depth = labs(Img.Raster->GetPlanes());

  Ldblk = ((uint32_t)Header.ras_depth*Header.ras_width+7) / 8;
  padding = Ldblk & 1;

  Header.ras_length = Header.ras_height * (padding + Ldblk);
  Header.ras_type = 0001;	// Standard
  if(Img.Palette!=NULL && Img.Palette->GetSize1D()>0)
  {
    Header.ras_maptype = 1;
    Header.ras_maplength = 3*Img.Palette->GetSize1D();
  }
  else
  {
    Header.ras_maptype = 0;
    Header.ras_maplength = 0;
  }
  if(SaveSUNRASHeader(f,Header) != 32)
    {Result=-1; goto FINISH;}

  if(Header.ras_maptype!=0 && Header.ras_maplength!=0)
  {
    int k = Img.Palette->GetPlanes()/3 - 8;
    if(k<0) k = 0;
    for(i=0; i<Img.Palette->GetSize1D(); i++) fputc(Img.Palette->R(i)>>k,f);
    for(i=0; i<Img.Palette->GetSize1D(); i++) fputc(Img.Palette->G(i)>>k,f);
    for(i=0; i<Img.Palette->GetSize1D(); i++) fputc(Img.Palette->B(i)>>k,f);
  }

  for(i=0; i<Header.ras_height; i++)
  {
    char *const pRow = (char *)Img.Raster->GetRow(i);
    if(pRow==NULL) break;
    if(Header.ras_depth==24)
        RGB_BGR(pRow,Img.Raster->GetSize1D());
    Result = fwrite(pRow,Ldblk,1,f);
    if(Header.ras_depth==24)
        RGB_BGR(pRow,Img.Raster->GetSize1D());
    if(Result!=1) {Result=-2; goto FINISH;}
    if(padding>0) fwrite(&padding,1,1, f);
  }

  Result = 0;
FINISH:
  fclose(f);
return Result;
}

#endif

#endif
//-----------------End of RAS-SUN routines---------------


//-------------------------RAW----------------------------
#ifdef SupportRAW

#if SupportRAW>=3
int SavePictureRAW(const char *Name, const Image &Img)
{
FILE *f;
uint16_t ldblk;
uint16_t i;

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

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

  ldblk=((long)labs(Img.Raster->GetPlanes())*Img.Raster->GetSize1D()+7) / 8;
  for(i=0;i<Img.Raster->Size2D;i++)
    {
    if(fwrite(Img.Raster->GetRow(i),ldblk,1,f)!=1)
      {
      fclose(f);
      return -1;
      }
    // if AlineProc<>nil then AlineProc^.NextLine;
    }

  fclose(f);
  return 0;
}
#endif

#endif
//-------------------End of RAW routines------------------


//-------------------------SGI----------------------------
#ifdef SupportSGI

// https://en.wikipedia.org/wiki/Silicon_Graphics_Image
struct SGIHeader
{
 uint16_t magic;
 uint8_t compression;
 uint8_t bytes_per_pixel;
 uint16_t dimension;
 uint16_t xsize;
 uint16_t ysize;
 uint16_t channels;
 uint32_t pix_min;
 uint32_t pix_max;
 uint32_t reserved;
 char image_name[80];
 uint32_t ColorMapID;
};

#if SupportSGI>=4 || SupportSGI==2

inline long LoadSgiSHeader(FILE *f, SGIHeader &SU)
{
#if defined(__PackedStructures__) && defined(HI_ENDIAN)
 return(fread(&SU,1,sizeof(SU),f));
#else
 return(loadstruct(f,"WBBWWWWDDDa80D",
          &SU.magic, &SU.compression, &SU.bytes_per_pixel, &SU.dimension, 
          &SU.xsize, &SU.ysize, &SU.channels, &SU.pix_min, &SU.pix_max, &SU.reserved,
          SU.image_name, &SU.ColorMapID));
#endif
}


bool SGIDecode(Raster1DAbstract *R1A, uint8_t *RLE_data, uint32_t RLE_Size)
{
uint8_t pixel, count;
uint32_t x = 0;
  if(R1A==NULL) return false;

  if(R1A->GetPlanes()==8)
  {
    while(RLE_Size-- > 0)
    {
      pixel = *RLE_data++;
      count = (pixel & 0x7fU);
      if(count == 0) break;
      if(count+x > R1A->Size1D) return false;
      if(pixel & 0x80)
      {
        if(RLE_Size < count) return false;
        RLE_Size -= count;        
        memcpy((uint8_t*)R1A->Data1D+x,RLE_data,count);
        RLE_data +=count;
      }
      else
      {
        if(RLE_Size-- < 1) return false;
        memset((uint8_t*)R1A->Data1D+x, *RLE_data++, count);
      }
      x += count;
    }
    return true;
  }
  //if(R1A->GetPlanes()==16)
  //{
  //}
  return false;
}


Image LoadPictureSGI(const char *Name)
{
FILE *f;
char channel;
SGIHeader Header;
Image Img;
Raster2DAbstract *Raster=NULL;
uint32_t LdBlk;
uint32_t y;

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

  if(LoadSgiSHeader(f,Header) != 108) goto FINISH;
  if(fseek(f, 404, SEEK_CUR)) goto FINISH;

  if(Header.magic != 0x01DA)
  {
    //LoadPictureSUNRAS=ErrAnother;
    goto FINISH;		//incorrect identifier
  }
  if(Header.compression > 1)
      goto FINISH;		//unknown compression

  switch(Header.channels)
  {
    case 1: if(Header.dimension==2)
              Raster = CreateRaster2D(Header.xsize,Header.ysize,8*Header.bytes_per_pixel);
            break;
    case 3: if(Header.dimension==3) 
	        Raster = CreateRaster2DRGB(Header.xsize,Header.ysize,8*Header.bytes_per_pixel);
            break;
    case 4: if(Header.dimension==3)
                Raster = CreateRaster2DRGBA(Header.xsize,Header.ysize,8*Header.bytes_per_pixel);
            break;
    default: goto FINISH;
  }

  if(Raster==NULL) goto FINISH;
  //if(AlineProc!=NULL) AlineProc^.InitPassing(p.y,'Loading IRIS SGI');

  LdBlk = Header.bytes_per_pixel * Raster->Size1D;

  if(Header.compression == 0)
  {
    if(Header.channels==1)
    {
      y = Header.ysize;
      while(y-- > 0)    
      {
        if(fread(Raster->GetRow(y),LdBlk,1,f)!=1) goto FINISH;
      }
    }
    else
    {
      Raster1DAbstract * const R1A = CreateRaster1D(Raster->Size1D,8*Header.bytes_per_pixel);
      if(R1A==NULL || R1A->Size1D==0) {delete Raster; Raster=NULL; goto FINISH;}

      for(channel=0; channel<Header.channels; channel++)
      {
        y = Header.ysize;
        while(y-- > 0)
        {
          if(fread(R1A->Data1D,LdBlk,1,f)!=1) break;
          Raster1DAbstract *RR = Raster->GetRowRaster(y);	// 
          if(RR==NULL) break;
          switch(Header.bytes_per_pixel)
          {
            case 1: Raster->Join8Bit(R1A->Data1D, 8*channel); break;
            //case 2: Raster->Join16Bit(R1A->Data1D, 16*channel); break;
          }
        }
      }
      delete R1A;
    }
  }
  else		// Imager is compressed.
  {
    Raster1DAbstract * R1A = NULL;
    unsigned int data_order;
    uint32_t *offsets, *runlength;
    size_t max_packets_alloc_size,
            rle_alloc_size,
            rle_dimensions;
    size_t here;
    uint32_t z;
    unsigned offset = 0;

    rle_dimensions = Header.ysize * Header.channels;
    rle_alloc_size = rle_dimensions * sizeof(uint32_t);

    if(rle_dimensions==0 || rle_alloc_size==0) goto FINISH;

          // Read runlength-encoded image format.
    //  if (TellBlob(image)+rle_alloc_size > (size_t) file_size) ThrowSGIReaderException(CorruptImageError,UnexpectedEndOfFile,image);
    offsets = (uint32_t*)malloc(sizeof(uint32_t)*rle_alloc_size);
    if(offsets==NULL) goto FINISH;

    const size_t file_size = FileSize(f);

    if(fread(offsets,1,rle_alloc_size,f) != rle_alloc_size) 
        goto FINISH_UNALLOC;
/*
          if (TellBlob(image)+rle_alloc_size > (size_t) file_size)
            ThrowSGIReaderException(CorruptImageError,UnexpectedEndOfFile,image);
*/
    runlength = (uint32_t*)malloc(sizeof(uint32_t)*rle_alloc_size);
    if(runlength == NULL)
        goto FINISH_UNALLOC;
        
    if(fread(runlength,1,rle_alloc_size,f) != rle_alloc_size)
        goto FINISH_UNALLOC2;

#ifndef HI_ENDIAN
    swab32((unsigned char*)offsets,rle_dimensions);
    swab32((unsigned char*)runlength,rle_dimensions);
#endif
    here = ftell(f);
    
    for(size_t i=0; i<rle_dimensions; i++)
    {
      if(offsets[i]<here || offsets[i]>file_size)
             goto FINISH_UNALLOC2;
      if(runlength[i] > ((size_t) 4U*Header.xsize+10U))
	     goto FINISH_UNALLOC2;             
    }

    {
    max_packets_alloc_size = (size_t)malloc((Header.xsize+10)*4U);
    unsigned char *RLE_packet = (unsigned char *)malloc(sizeof(unsigned char *)*max_packets_alloc_size);
    if(RLE_packet == NULL) goto FINISH_UNALLOC2;

		// Check data order.
    data_order = 0U;
    for(y=0; ((y<Header.ysize) && !data_order); y++)
      for(z=0U; ((z < Header.channels) && !data_order); z++)
      {
        const size_t run_index = (size_t)y + z*Header.ysize;
        if(run_index >= rle_alloc_size)
           goto FINISH_UNALLOC2;
        if(offsets[run_index] < offset)
                  data_order=1;
        offset = offsets[run_index];
      }
  
    if(Header.channels > 1)
    {
      R1A = CreateRaster1D(Raster->Size1D,8*Header.bytes_per_pixel);
      if(R1A==NULL || R1A->Size1D==0) {delete Raster; Raster=NULL; goto FINISH;}
    }

    if(data_order == 1)
    {
      for(z=0U; z<Header.channels; z++)
      {
          for(y=0U; y<Header.ysize; y++)
          {
            const size_t run_index = (size_t)y + z*Header.ysize;
            size_t length;
            if(run_index >= rle_alloc_size) goto FINISH_UNALLOC3;

            if(fseek(f,offsets[run_index],SEEK_SET) != 0) goto FINISH_UNALLOC3;
            length = runlength[run_index];
            if(length > max_packets_alloc_size) goto FINISH_UNALLOC3;
            
            if(fread(RLE_packet,length,1,f) != 1) goto FINISH_UNALLOC3;
            //offset += length;

             if(R1A)
             {
               SGIDecode(R1A,RLE_packet,length);
               if(Header.bytes_per_pixel==1)
                 Raster->GetRowRaster(Header.ysize-y-1)->Join8Bit(R1A->Data1D,8*z);
	       //else
               //  Raster->GetRowRaster(y)->Join16Bit(R1A->Data1D,16*z);
             }
             else
             {
               SGIDecode(Raster->GetRowRaster(Header.ysize-y-1),RLE_packet,length);
             }
           }
         }
       }
       else
       {
         for(y=0; y<Header.ysize; y++)
         {
           for(z=0; z<Header.channels; z++)
           {
             const size_t run_index = (size_t)y + z*Header.ysize;
             size_t length;
             if(run_index >= rle_alloc_size) goto FINISH_UNALLOC3;
                        
             if(fseek(f,offsets[run_index],SEEK_SET) != 0) goto FINISH_UNALLOC3;

             length = runlength[run_index];
             if(length > max_packets_alloc_size) goto FINISH_UNALLOC3;

             if(fread(RLE_packet,length,1,f) != 1) goto FINISH_UNALLOC3;
             //offset+=length;

             if(R1A)
             {
               SGIDecode(R1A,RLE_packet,length);
               if(Header.bytes_per_pixel==1)
                 Raster->GetRowRaster(Header.ysize-y-1)->Join8Bit(R1A->Data1D,8*z);
	       //else
               //  Raster->GetRowRaster(y)->Join16Bit(R1A->Data1D,16*z);
             }
             else
             {
               SGIDecode(Raster->GetRowRaster(Header.ysize-y-1),RLE_packet,length);
             }
           }
         if(feof(f)) break;
       }
     }

FINISH_UNALLOC3:
    free(RLE_packet);
    }
FINISH_UNALLOC2:
    free(runlength);
FINISH_UNALLOC:
    free(offsets);

    if(R1A) delete R1A;
  }

FINISH:
  fclose(f);
  Img.AttachRaster(Raster);
  return(Img);
}
#endif


#if SupportSGI>=3

inline long SaveSgiSHeader(FILE *f, const SGIHeader &SU)
{
#if defined(__PackedStructures__) && defined(HI_ENDIAN)
 return(fwrite(&SU,1,sizeof(SU),f));
#else
 return(savestruct(f,"WBBWWWWDDDa80D",
          SU.magic, SU.compression, SU.bytes_per_pixel, SU.dimension, 
          SU.xsize, SU.ysize, SU.channels, SU.pix_min, SU.pix_max, SU.reserved,
          SU.image_name, SU.ColorMapID));
#endif
}

int SavePictureSGI(const char *Name, const Image &Img)
{
FILE *f;
uint16_t ldblk;
uint16_t i;
SGIHeader Header;

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

  memset(&Header,0,sizeof(Header));
  Header.magic = 0x01DA;
  //Header.compression = 0;
  Header.xsize = Img.Raster->Size1D;
  Header.ysize = Img.Raster->Size2D;
  switch(Header.channels=Img.Raster->Channels())
  {
    case 1: Header.dimension=2;
            break;
    case 3: Header.dimension=3;
            break;
    case 4: Header.dimension=3;
            break;
    default: return(-10);
  }
  Header.bytes_per_pixel = Img.Raster->GetPlanes() / (8*Header.channels);
  //Header.pix_min=0;
  if(Header.bytes_per_pixel==1)
      Header.pix_max=0xFF;
  else
      Header.pix_max=0xFFFF;

  if((f=fopen(Name,"wb"))==NULL) return(ErrOpenFile);
  if(SaveSgiSHeader(f,Header) != 108) {fclose(f);return ErrWriteFile;}
  i = 404;
  do
  {
    if(i<=sizeof(Header.image_name))
    {
      fwrite(Header.image_name,1,i,f);
      break;
    }
    fwrite(Header.image_name,1,sizeof(Header.image_name),f);
    i-=sizeof(Header.image_name);
  } while(i>0);

  ldblk = Header.bytes_per_pixel * Header.xsize;

  if(Header.dimension==2)
  {
    i = Header.ysize;
    while(i-- > 0)    
    {
      if(fwrite(Img.Raster->GetRow(i),ldblk,1,f)!=1)
         {fclose(f);return ErrWriteFile;}
    }
  }
  else
  {
    Raster1DAbstract * const R1A = CreateRaster1D(Img.Raster->Size1D,8*Header.bytes_per_pixel);
    for(char channel=0; channel<Header.channels; channel++)
    {
      i = Header.ysize;
      while(i-- > 0)      
      {
        Raster1DAbstract *RR = Img.Raster->GetRowRaster(i);
        if(RR==NULL) break;
        RR->Peel8Bit(R1A->Data1D, 8*channel);
        if(fwrite(R1A->Data1D,ldblk,1,f)!=1)
            {fclose(f);delete R1A;return ErrWriteFile;}
      }
    }
    delete R1A;
  }

  fclose(f);
  return 0;
}

#endif


#endif
//-------------------End of SGI routines------------------


//-------------------------TIFF----------------------------
#ifdef SupportTIFF

// https://www.loc.gov/preservation/digital/formats/content/tiff_tags.shtml
// https://www.fileformat.info/format/tiff/corion.htm

#define TIFFTAG_EXIFIFD 34665 /* Pointer to EXIF private directory */

#define COMPRESSION_DEFLATE 32946   /* Deflate compression, legacy tag */
#define COMPRESSION_DEFLATE_ADOBE 8   /* Deflate compression, legacy tag */

struct TIFHeader
	{
	uint8_t CharId[2];
	uint16_t CharV;
	uint32_t Pos;
	};

struct TIFDirectory
	{
	uint16_t Tag,Field;
	uint32_t Long2,Value;
	};

#if SupportTIFF>=4 || SupportTIFF==2

Image LoadPictureTIFF(const char *Name)
{
uint32_t *StripsPtrs;
FILE *f;
uint16_t Ldblk;
int i;
TIFHeader Header;
TIFDirectory IFD;
uint16_t EntryCount, SizeX, SizeY;
uint32_t  PalettePos, NextPtr;
uint32_t BitsPerSample;
uint16_t SamplesPerPixel;
uint16_t *pBitsPerSample = NULL;
uint16_t Compression;
uint16_t PhotometricInterpretation;
uint32_t StripOffsets;
uint16_t StripCount;
uint16_t Orientation;
uint16_t RowsPerStrip,PlanarCfg;
int (*RdWORD_TIFF)( uint16_t *num, FILE *f );
int (*RdDWORD_TIFF)( uint32_t *num, FILE *f );
const char *IFD_Marshall = NULL;
Raster2DAbstract *Raster = NULL;
APalette *Palette = NULL;
Image Img;
Image *CurImg = &Img;
uint32_t EXIFOffset=0;
/*
#if defined(HasZLIB)
void *ZipBuffer = NULL;
z_stream zip_info;
int zip_status;
#endif
*/

StripsPtrs=NULL;
PalettePos=0;
BitsPerSample=1;
SamplesPerPixel=1;
Compression=1;
StripOffsets=0;
StripCount=0;
PlanarCfg=0;
RowsPerStrip=65535;

 if((f=fopen(Name,"rb"))==NULL) return(Img);
 if(fread(&Header,2,1,f)!=1) goto FINISH_IMAGE;

 RdWORD_TIFF=NULL; RdDWORD_TIFF=NULL;
 if(Header.CharId[0]=='I' && Header.CharId[1]=='I')
	{
	RdWORD_TIFF=RdWORD_LoEnd;
	RdDWORD_TIFF=RdDWORD_LoEnd;
	IFD_Marshall="wwdd";
	}
 if(Header.CharId[0]=='M' && Header.CharId[1]=='M')
	{
	RdWORD_TIFF=RdWORD_HiEnd;
	RdDWORD_TIFF=RdDWORD_HiEnd;
	IFD_Marshall="WWDD";
	}

 if(RdWORD_TIFF==NULL || RdDWORD_TIFF==NULL ) goto FINISH_IMAGE;
 RdWORD_TIFF(&Header.CharV,f);
 RdDWORD_TIFF(&Header.Pos,f);

 if (Header.CharV!=42) goto FINISH_IMAGE;	/*check of version*/

 NextPtr = Header.Pos;
 while(NextPtr > 0)
 {
   if(fseek(f, NextPtr, SEEK_SET)!=0) break;

   PhotometricInterpretation = 0;
   Orientation = 0;
   RdWORD_TIFF(&EntryCount,f);
   SizeX = SizeY = 0;
   for(i=1; i<=EntryCount; i++)
	{
	loadstruct(f,IFD_Marshall,&IFD.Tag,&IFD.Field,&IFD.Long2,&IFD.Value);

	if(IFD.Field==3)	// 16bit UINT
	{
	  if(Header.CharId[0]=='M')
	  {
	    if(IFD.Long2>=65536) IFD.Long2>>=16;
	    if(IFD.Value>=65536) IFD.Value>>=16;
	  }
/*	  else
	  {
	    IFD.Long2&=0xFFFF;
	    IFD.Value&=0xFFFF;
	  } */
	}
#ifdef _DEBUG
        fprintf(stderr,"\n%Xh Tag=%d, field=%u, value=%u,long2=%u", ftell(f)-12, IFD.Tag, IFD.Field, IFD.Value, IFD.Long2);
#endif

	switch(IFD.Tag)
	   {
//		255:;				{SubFileType}
	   case 256:SizeX=IFD.Value;break;	//ImageWidth   $0100
	   case	257:SizeY=IFD.Value;break;	//ImageLength  $0101
	   case	258:BitsPerSample=IFD.Value;break; // $102
	   case	259:Compression=IFD.Value;break; //Compression  $0103
	   case 262:PhotometricInterpretation=IFD.Value;break;	//{PhotometricInterpretation}
//		266:;				{FillOrder}
	   case	273:StripOffsets=IFD.Value;	//StripOffsets $111
		    StripCount=IFD.Long2;
		    break;
	  case 274:Orientation = (IFD.Value>8)?0:IFD.Value;	//Orientation
	  case 277:SamplesPerPixel=IFD.Value;break;//SamplesPerPixel
	  case 278:RowsPerStrip=IFD.Value;break;//RowsPerStrip
//		280:;				{MinSampleValue
//		281:;				{MaxSampleValue
//		282:;				{XResolution}
//		283:;				{YResolution}
	  case 284:PlanarCfg=IFD.Value;break;	//PlanarConfiguration
	  case 320:PalettePos=IFD.Value;break; //Palette-color map
	  case TIFFTAG_EXIFIFD:
				EXIFOffset=IFD.Value;
				break;
	   }
	}

   RdDWORD_TIFF(&NextPtr,f);
   //i = ftell(f);	//!!!
   if(StripOffsets==0 || RowsPerStrip==0) goto FINISH_FRAME;
   if(((SizeY-1) / RowsPerStrip)+1 != StripCount) goto FINISH_FRAME;

/*
#if defined(HasZLIB)
 if(Compression==COMPRESSION_DEFLATE_ADOBE)
 {
   memset(&zip_info,0,sizeof(zip_info));
   zip_status = inflateInit(&zip_info);
   if(zip_status != Z_OK)
       goto FINISH;
   ZipBuffer = malloc((SizeX*BitsPerSample+7)/8);
 }
 else
#endif
*/
     if(Compression!=1)		// https://www.awaresystems.be/imaging/tiff/tifftags/compression.html
     {
#ifdef _DEBUG
       fprintf(stderr,"\nUnsupported compression %u\n", Compression);
#endif
       goto FINISH_FRAME;
     }

   if(SamplesPerPixel>1)
   {
     pBitsPerSample = (uint16_t*)malloc(sizeof(uint16_t)*SamplesPerPixel);
     if(pBitsPerSample==NULL) goto FINISH_FRAME;
     fseek(f,BitsPerSample,SEEK_SET);
     BitsPerSample = 0;
     for(i=0; i<SamplesPerPixel; i++)
     {
       RdWORD_TIFF(&(pBitsPerSample[i]),f);
       BitsPerSample += pBitsPerSample[i];
     }
   }

   if(BitsPerSample>64) goto FINISH_FRAME;

   switch(SamplesPerPixel)
   {
     case 4: Raster=CreateRaster2DRGBA(SizeX,SizeY,BitsPerSample/4);
             break;
     case 3: Raster=CreateRaster2DRGB(SizeX,SizeY,BitsPerSample/3);
             break;
     case 1: Raster=CreateRaster2D(SizeX,SizeY,BitsPerSample);
             break;
   }
   if(Raster==NULL) goto FINISH_FRAME;
   /* if(AlineProc<>nil)AlineProc^.InitPassing(Raster->Size2D,'Loading TIF');*/

   if(StripCount==0)  goto FINISH_FRAME;
   if((StripsPtrs=(uint32_t *)calloc(StripCount,sizeof(uint32_t)))==NULL ) goto FINISH_FRAME;
   if(StripCount>1)		/*more strips*/
	{
	fseek(f,StripOffsets,SEEK_SET);
	for(i=0; i<StripCount; i++)
		RdDWORD_TIFF(&StripsPtrs[i],f);
	}
   else {
	StripsPtrs[0] = StripOffsets;
	}
   fseek(f,StripsPtrs[0],SEEK_SET);

   Ldblk = ((long)BitsPerSample*SizeX+7) / 8;
/*
#if defined(HasZLIB)
 if(Compression==COMPRESSION_DEFLATE_ADOBE)
 {
   i = 0;
   fseek(f,StripsPtrs[0],SEEK_SET);
   if(fread(ZipBuffer,Ldblk,1,f)!=1) goto FINISH_FRAME;
   zip_info.next_in = (Bytef*)ZipBuffer;
   zip_info.avail_in = Ldblk;
   while(i<SizeY)
   {
     zip_info.next_out = (Bytef*)Raster->GetRow(i);
     zip_info.avail_out = Ldblk;
     zip_status = inflate(&zip_info,Z_NO_FLUSH);
     if(zip_status!=Z_OK)
     {
#ifdef _DEBUG
       if(zip_info.msg) fprintf(stderr,"LoadPictureTIFF %s\n",zip_info.msg);
#endif
       goto FINISH_FRAME;
     }
   }
 }
 else
#endif
*/
   for(i=0; i<SizeY; i++)
   {
     if(i % RowsPerStrip == 0)
         fseek(f,StripsPtrs[i/RowsPerStrip],SEEK_SET);
     void *pRow = Raster->GetRow(i);
     if(fread(pRow,Ldblk,1,f)!=1) {goto FINISH_FRAME;}
     if(PhotometricInterpretation==0)	// Gray level - 0 is  imaged as white.
       NotR((char*)pRow,Ldblk);
     //if AlineProc<>nil then AlineProc^.NextLine;
     if(feof(f)) break;
   }

   if(PalettePos!=0)		/*load of the palette*/
   {
	fseek(f,PalettePos,SEEK_SET);
	Ldblk=(1 << BitsPerSample);
	Palette = BuildPalette(Ldblk,16);
	for(i=0;i<Ldblk;i++)
		{
		RdWORD_TIFF(&Compression,f);
		Palette->setR(i,Compression);
		}
	for(i=0;i<Ldblk;i++)
		{
		RdWORD_TIFF(&Compression,f);
		Palette->setG(i,Compression);
		}
	for(i=0;i<Ldblk;i++)
		{
		RdWORD_TIFF(&Compression,f);
		Palette->setB(i,Compression);
		}

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

#if 0
 if(EXIFOffset>0)
 {
//   PropertyItem *PropExif = new PropertyItem("Exif");
//   PropExif->Data = malloc(EXIFSize);
//   if(PropExif->Data==NULL) {delete PropExif; goto FINISH_FRAME;}
//   PropExif->DataSize = EXIFSize;

   fseek(f,EXIFOffset,SEEK_SET);
   RdWORD_TIFF(&EntryCount,f);
   while(EntryCount > 0)
   {
     if(feof(f)) break;
     loadstruct(f,IFD_Marshall,&IFD.Tag,&IFD.Field,&IFD.Long2,&IFD.Value);
     fprintf(stderr,"\n%X EXIF Tag=%d, field=%u, value=%u,long2=%u", ftell(f)-12, IFD.Tag, IFD.Field, IFD.Value, IFD.Long2);
     EntryCount--;
   }
   RdDWORD_TIFF(&NextPtr,f);
/*
   if(fread(PropExif->Data,PropExif->DataSize,1,f)!=1)
       {delete PropExif; goto FINISH_FRAME;}
   OffsetExif((unsigned char *)PropExif->Data, PropExif->DataSize, -EXIFOffset);
   Img.AttachProperty(PropExif);
*/
 }
#endif

FINISH_FRAME:
   if(pBitsPerSample!=NULL) {free(pBitsPerSample);pBitsPerSample=NULL;}
   if(StripsPtrs!=NULL) {free(StripsPtrs);StripsPtrs=NULL;}
/*
#if defined(HasZLIB)
 if(ZipBuffer)
 {
   inflateEnd(&zip_info);			// Release all caches used by zip.
   free(ZipBuffer);ZipBuffer=NULL;
 }
#endif
*/

   if(!CurImg->isEmpty())
   {
     CurImg->Next = new Image();
     CurImg = CurImg->Next;
   }
   if(Raster)
   {
     switch(Orientation)
     {
       case 0: break;
       case 1: break;	// The 0th row represents the visual top of the image, and the 0th column represents the visual left-hand side.
       case 2: Flip1D(Raster);	// The 0th row represents the visual top of the image, and the 0th column represents the visual right-hand side.
               break;
       case 3: Flip2D(Raster);	// The 0th row represents the visual bottom of the image, and the 0th column represents the visual right-hand side.
               break;
       case 4: Flip1D(Raster);	// The 0th row represents the visual bottom of the image, and the 0th column represents the visual left-hand side.
               Flip2D(Raster);
               break;
/*	TODO: 90 degs rotation needed.
       case 5:		// The 0th row represents the visual left-hand side of the image, and the 0th column represents the visual top.
       case 6:		// The 0th row represents the visual right-hand side of the image, and the 0th column represents the visual top.
       case 7:		// The 0th row represents the visual right-hand side of the image, and the 0th column represents the visual bottom.
       case 8:		// The 0th row represents the visual left-hand side of the image, and the 0th column represents the visual bottom. 
               break;
*/
     }
     CurImg->AttachRaster(Raster);
     Raster=NULL;
   }
   CurImg->AttachPalette(Palette); Palette=NULL;
 }

FINISH_IMAGE:
 fclose(f);

 return(Img);
}  /*LoadTIF*/

#endif


#if SupportTIFF>=3


inline long SaveHeaderTIFF(FILE *f, const TIFHeader &HDR)
{
#if defined(__PackedStructures__)
 return(fwrite(&HDR,1,sizeof(HDR),f));
#else
 return(savestruct(f,"wwd",
	 *(uint16_t*)HDR.CharId, HDR.CharV, HDR.Pos));
#endif
}

inline long SaveTagTIFF(FILE *f, const TIFDirectory &TAG)
{
#if defined(__PackedStructures__)
 return(fwrite(&TAG,1,sizeof(TAG),f));
#else
 return(savestruct(f,"wwdd",
	 TAG.Tag, TAG.Field, TAG.Long2, TAG.Value));
#endif
}


int SavePictureTIFF(const char *Name, const Image &Img)
{
FILE *f;
TIFHeader Header;
TIFDirectory IFD;
uint32_t StripOffsets;
uint16_t EntryCount;
long ldblk;
unsigned i;
const Image *CurImg = &Img;

  while(CurImg->Raster==NULL)
  {
    CurImg = CurImg->Next;
    if(CurImg==NULL) return(ErrEmptyRaster);
  }
  if((f=fopen(Name,"wb"))==NULL) return(ErrOpenFile);

  memset(Header.CharId, 'I', 2);
  Header.CharV = 42;
  Header.Pos = 8;
  SaveHeaderTIFF(f,Header);

  EntryCount = 0;
  StripOffsets = 8;			// place after TIFF header.

  do
  {
    for(uint8_t Stage=0; Stage<3; Stage++)	// 0 calc sizes; 1 write IFD; 2 write data
    {
      if(Stage==1) WrWORD_LoEnd(EntryCount,f);

      EntryCount = 5;
      if(Stage==1)
      {
        IFD.Long2 = 1;
        IFD.Field = 3;
        IFD.Tag=256; IFD.Value=CurImg->Raster->Size1D;	//ImageWidth   $0100
        SaveTagTIFF(f,IFD);
        IFD.Tag=257; IFD.Value=CurImg->Raster->Size2D;	//ImageLength  $0101
        SaveTagTIFF(f,IFD);
        IFD.Field = 3;
        IFD.Tag=259; IFD.Value=1; IFD.Long2=1;		//Compression  $0103  1 - Uncompressed
        SaveTagTIFF(f,IFD);
        IFD.Tag=262;
        if(CurImg->Raster->Channels()==1)
        {
          if(CurImg->Palette!=NULL)
              IFD.Value=3;
          else
              IFD.Value=1;		// 1 =  For bilevel  and grayscale  images:   0 is  imaged as black.
        }
        else
            IFD.Value=2;		// RGB
        SaveTagTIFF(f,IFD);
        IFD.Tag=277; IFD.Field=3; IFD.Value=CurImg->Raster->Channels();		//SamplesPerPixel
        SaveTagTIFF(f,IFD);
      }

      EntryCount++;
      if(Stage==1)
      {
        IFD.Tag = 258;					//BitsPerSample  $102
        if((IFD.Long2=CurImg->Raster->Channels())==1)
        {
          IFD.Value = CurImg->Raster->GetPlanes();
        }
        else
        {
          IFD.Value = StripOffsets;
          StripOffsets += 2 * CurImg->Raster->Channels();
      }
      IFD.Field = (IFD.Value<=0xFFFF) ? 3 : 4;
      SaveTagTIFF(f,IFD);
    }
    if(Stage==2 && CurImg->Raster->Channels()>1)
    {
      const uint16_t ChanCount = CurImg->Raster->GetPlanes()/CurImg->Raster->Channels();
      for(i=0; i<CurImg->Raster->Channels(); i++)
        WrWORD_LoEnd(ChanCount, f);
    }

    EntryCount++;
    ldblk = (labs((long)CurImg->Raster->GetPlanes())*CurImg->Raster->GetSize1D()+7) / 8;
    switch(Stage)
    {
      case 1: IFD.Tag=273; IFD.Field=4; IFD.Value=StripOffsets; IFD.Long2=1;	//StripOffsets $111
              SaveTagTIFF(f,IFD);
              StripOffsets += ldblk * CurImg->Raster->Size2D;
              break;
      case 2: for(i=0; i<CurImg->Raster->Size2D; i++)
              {
                const void *pRow = CurImg->Raster->GetRow(i);
                if(pRow==NULL) return -1;
                if(fwrite(pRow,ldblk,1,f)!=1)
                {
                  fclose(f);
                  return -2;
                }
                // if AlineProc<>nil then AlineProc^.NextLine;
             }
             break;
    }

    if(CurImg->ImageType()==ImagePalette)
    {
      ldblk = (1 << CurImg->Raster->GetPlanes());
      EntryCount++;
      switch(Stage)
      {
        case 1:
              IFD.Tag = 320;
              IFD.Value = StripOffsets;		//Palette-color map
              IFD.Field = 3;			// WORD array
              IFD.Long2 = 3 * ldblk;		// amount of words.
              SaveTagTIFF(f,IFD);
              StripOffsets += IFD.Long2 * 2;	// amount of bytes.
              break;
        case 2:
              {
                unsigned k;
                switch(CurImg->Palette->GetPlanes()/CurImg->Palette->Channels())
                {
                  case 8: k=0x101; break;
                  default:
                  case 16: k=0x1; break;
                }
                for(i=0; i<ldblk; i++)
                {
                  WrWORD_LoEnd(CurImg->Palette->R(i)*k,f);
                }
                for(i=0; i<ldblk; i++)
                {
                  WrWORD_LoEnd(CurImg->Palette->G(i)*k,f);
                }
                for(i=0; i<ldblk; i++)
                {
                  WrWORD_LoEnd(CurImg->Palette->B(i)*k,f);
                }
              }
        }
      }

      if(Stage==0) StripOffsets += 2 + EntryCount*12 + 4;
      if(Stage==1)
      {
        if(CurImg->Next!=NULL && CurImg->Next->Raster!=NULL)
          WrDWORD_LoEnd(StripOffsets,f);			// NextPtr
        else
          WrDWORD_LoEnd(0,f);				// NextPtr (end ptr)
      }
    }

    CurImg = CurImg->Next;
  } while(CurImg!=NULL);

  fclose(f);
  return 0;
}

#endif



#endif
//-------------------End of TIFF routines------------------


//--------------------------TGA----------------------------
#ifdef SupportTGA

// http://www.paulbourke.net/dataformats/tga/
// https://www.fileformat.info/format/tga/sample/index.htm

struct TGAHeader
	{
	uint8_t IdentifLen;
	uint8_t ColorMapType;
	uint8_t ImageCodeType;	///< Compression and color types (0-No Image Data Included. 1-Uncompressed, Color mapped image, 2-Uncompressed, True Color Image, 
				///<   3-Uncompressed, black and white images, 9-Run-length encoded, Color mapped image, 10 - Runlength encoded RGB images, 11-Run-Length encoded, Black and white image)
	uint16_t CMapOrigin;
	uint16_t CMapLength;
	uint8_t CMapEntrySize;

	uint16_t XOrigin;
	uint16_t YOrigin;
	uint16_t Width;
	uint16_t Height;
	uint8_t ImagePixSize;
	uint8_t ImageDescByte;	///<  Bits 0 through 3 specify the number of attribute bits per pixel.
				///<  Bits 5 and 4 contain the image origin location. Bit 4 is for left-to-right ordering and bit 5 is for top-to-bottom ordering.
	};

typedef enum
{
	TGAColormap = 1,	///< Colormapped image data.
	TGARGB = 2,		///< Truecolor image data.
	TGAMonochrome = 3,	///< Monochrome image data.
	TGARLEColormap = 9,	///< Colormapped image data (encoded).
	TGARLERGB = 10,		///< Truecolor image data (encoded).
	TGARLEMonochrome = 11	///< Monochrome image data (encoded).
} E_TGA_FORMATS;


#if SupportTGA>=4 || SupportTGA==2

inline long LoadTgaHeader(FILE *f, TGAHeader &SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(SU),f));
#else
 return(loadstruct(f,"bbbwwbwwwwbb",
	&SU.IdentifLen, &SU.ColorMapType, &SU.ImageCodeType,
	&SU.CMapOrigin, &SU.CMapLength, &SU.CMapEntrySize,
	&SU.XOrigin, &SU.YOrigin, &SU.Width, &SU.Height,
	&SU.ImagePixSize, &SU.ImageDescByte));
#endif
}


Image LoadPictureTGA(const char *Name)
{
FILE  *f;
uint16_t ldblk;
uint16_t y, TrueY;
Raster2DAbstract *Raster=NULL;
APalette *Palette=NULL;
Image Img, *CurrImg;
TGAHeader Header;

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

  CurrImg = &Img;
  while(LoadTgaHeader(f,Header)==18)
  {
    if(Header.ColorMapType>=2) break;
    if(!(Header.ImageCodeType==TGAColormap || Header.ImageCodeType==TGARGB || Header.ImageCodeType==TGAMonochrome ||
        Header.ImageCodeType==TGARLEColormap || Header.ImageCodeType==TGARLERGB || 
        (Header.ImageCodeType==TGARLEMonochrome && Header.ImagePixSize>=8)))	// Less than 8bit RLE is a nonsense or unknown feature.
    {
      //LoadPictureTGA:=ErrAnother;
      break;
    }

	// ImagePixSize==16 has representation ARRRRRGG GGGBBBBB.
    if(Header.ImagePixSize==32)
      Raster = CreateRaster2DRGBA(Header.Width,Header.Height,8);
    else if(Header.ImagePixSize==24)
      Raster = CreateRaster2DRGB(Header.Width,Header.Height,8);
    else
      Raster = CreateRaster2D(Header.Width,Header.Height,Header.ImagePixSize);
    if(Raster==NULL) break;

    fseek(f,Header.IdentifLen,SEEK_CUR);
    if(feof(f)) break;

    // if AlineProc<>nil then AlineProc^.InitPassing(p.y,'Loading TGA');
    if(Header.ColorMapType==1)
    {
      const long FilePos = ftell(f);
      Palette = BuildPalette(1 << Header.ImagePixSize,8);
      if(Header.CMapEntrySize == 24)
      {		// Pozor Muze se zhroutit pri spatne tabulce
	fread((char *)Palette->Data1D + 3*Header.CMapOrigin, 3*Header.CMapLength,1,f);
	RGB_BGR((char *)Palette->Data1D + 3*Header.CMapOrigin,Header.CMapLength);
      }
      if(Header.CMapEntrySize == 16)
      {
        for(y=0; y<Header.CMapLength; y++)
        {
          uint16_t w;
          RdWORD_LoEnd(&w,f);
          Palette->setR(y,(w>>7)&0xF8);
          Palette->setG(y,(w>>2)&0xF8);
          Palette->setB(y,(w&0x1F)<<3);
        }
      }
      fseek(f,FilePos + (Header.CMapEntrySize / 8)*Header.CMapLength,SEEK_SET);
    }

    // if(Header.ImageCodeType==ImagePalette) ColorMode=ImagePalette;
    ldblk = (Raster->GetPlanes()*Raster->GetSize1D()+7)/8;

    TrueY = y = Header.Height;
    if(Header.ImageCodeType>=TGARLEColormap && Header.ImageCodeType<=TGARLEMonochrome)
    {
      uint8_t runlength = 0;
      uint8_t InterlaceBase = 2;
      uint32_t RGB = 0;
      while(y > 0)
      {
        y--;
        switch(3 & (Header.ImageDescByte>>6))
	{		// Support for interlacing.
	  case 0:
          case 3: TrueY = y; break;
          case 1: if(TrueY>1)
		      TrueY -= 2;
                  else
                     TrueY = Header.Height-2;
                  break;
          case 2: if(TrueY>3)
                      TrueY -= 4;
                  else
                  {
                    TrueY = Header.Height-InterlaceBase;
                    InterlaceBase++;
                  }
	}

        for(uint16_t x=0; x<Header.Width; x++)
        {
          if((0x7F&runlength) != 0)
          {
            if(runlength-- < 128) goto READ_PIX;
          }
          else
          {
	    runlength = fgetc(f);
		// For first time we need to read pixel in all cases.
READ_PIX:
	    switch(Header.ImagePixSize)
	    {
	      case  8: RGB=fgetc(f); break;
              case 16: RdWORD_LoEnd((uint16_t*)&RGB,f); break;
	      case 24: 
#ifdef LO_ENDIAN
                     fread(&RGB,3,1,f);
#elif defined HI_ENDIAN
                     fread(&RGB,3,1,f);
		     swab32((uint8_t*)&RGB,1);
#else
		     RGB = fgetc(f) | (fgetc(f)<<8) | (fgetc(f)<<16)
#endif
		     break;
	      case 32: RdDWORD_LoEnd(&RGB,f); break;
	    }
          }
        
          Raster->SetValue2D(x,TrueY,RGB);
        }
        if(Header.ImageCodeType==10 && Raster->GetPlanes()==24)
            RGB_BGR((char *)Raster->GetRow(TrueY),Raster->GetSize1D());
        // if AlineProc<>nil then AlineProc^.NextLine;
      }
    }
    else
    {
      uint8_t InterlaceBase = 2;
      while(y > 0)
      {
        y--;
        switch(3 & (Header.ImageDescByte>>6))
	{		// Support for interlacing.
	  case 0:
          case 3: TrueY = y; break;
          case 1: if(TrueY>1)
		      TrueY -= 2;
                  else
                     TrueY = Header.Height-2;
                  break;
          case 2: if(TrueY>3)
                      TrueY -= 4;
                  else
                  {
                    TrueY = Header.Height-InterlaceBase;
                    InterlaceBase++;
                  }
        }
        if(fread(Raster->GetRow(TrueY),ldblk,1,f)!=1) {break;}
        if(Header.ImageCodeType==2 && Raster->GetPlanes()==24)
            RGB_BGR((char *)Raster->GetRow(TrueY),Raster->GetSize1D());
        // if AlineProc<>nil then AlineProc^.NextLine;
      }
    }

    if((Header.ImageDescByte&16) == 16) Flip1D(Raster);
    if((Header.ImageDescByte&32) == 32) Flip2D(Raster); // Flop image when descriptor contains 32.

    if(CurrImg->Raster != NULL)
    {
      CurrImg->Next = new Image;
      CurrImg = CurrImg->Next;
    }
    CurrImg->AttachRaster(Raster);	Raster=NULL;
    CurrImg->AttachPalette(Palette);	Palette=NULL;
 }

 fclose(f);
 if(Raster) {delete(Raster); Raster=NULL;}
 if(Palette) {delete(Palette); Palette=NULL;}
 return(Img);
}
#endif


#if SupportTGA>=3

inline long SaveTgaHeader(FILE *f, const TGAHeader &SU)
{
#if defined(__PackedStructures__)
 return(fwrite(&SU,1,sizeof(SU),f));
#else
 return(savestruct(f,"bbbwwbwwwwbb",
	SU.IdentifLen, SU.ColorMapType, SU.ImageCodeType,
	SU.CMapOrigin, SU.CMapLength, SU.CMapEntrySize,
	SU.XOrigin, SU.YOrigin, SU.Width, SU.Height,
	SU.ImagePixSize, SU.ImageDescByte));
#endif
}


int SavePictureTGA(const char *Name, const Image &Img)
{
FILE *f;
TGAHeader Header;
uint16_t x, y;
const Image *CurrImg;

 if(Img.Raster==NULL) return(ErrEmptyRaster);
 if((f=fopen(Name,"wb"))==NULL) return(ErrOpenFile);
 CurrImg = &Img;

 Header.IdentifLen = 0;	//strlen(ImageID); //??? Start with the lenght of the Image ID string. ??
 Header.ImageDescByte = 0;	// Flip Y=32; - it is recommended not to use these attributes.

 do
 {
   if(CurrImg->Raster != NULL)
   {
     if(CurrImg->Palette!=NULL && CurrImg->Raster->GetPlanes()>=1 && CurrImg->Raster->GetPlanes()<=8)
     {
       Header.ColorMapType = 1;
       Header.ImageCodeType = TGAColormap;
       if(CurrImg->Raster->GetPlanes()==1)
           Header.ImagePixSize = 1;
       else
           Header.ImagePixSize = 8;

       Header.CMapLength = 1 << Header.ImagePixSize;
       Header.CMapEntrySize = 24;
     }
     else
     {
       Header.ColorMapType = 0;  //There's no color map here (0).
       switch(CurrImg->Raster->GetPlanes())
       {
         case 1:  Header.ImageCodeType = TGAMonochrome; //And then the Image Type. Uncompressed Monochromatic.
                  Header.ImagePixSize = 1;
                  break;
         case 2:
         case 4:
         case 8:  Header.ImageCodeType = TGAMonochrome; //And then the Image Type. Uncompressed Monochromatic.
                  Header.ImagePixSize = 8;
                  break;

         case 16: Header.ImageCodeType = TGARGB;	// Uncompressed ARRRRRGG GGGBBBBB.
                  Header.ImagePixSize = 16;
                  Header.ImageDescByte |= 1;
                  break;

         default: Header.ImageCodeType = TGARGB;	//Uncompressed True Color (2).
                  Header.ImagePixSize = 24;
                  break;
       }
       Header.CMapLength = 0;
       Header.CMapEntrySize = 0;
     }
     Header.CMapOrigin = 0;

     Header.XOrigin = Header.YOrigin = 0;
		/*  Now we should output the image specification. The last byte
		*  written (32) indicates that the first pixel is upper left on
		*  the screen. */
     Header.Width = CurrImg->Raster->GetSize1D();
     Header.Height = CurrImg->Raster->Size2D;

     SaveTgaHeader(f,Header);

     if(Header.IdentifLen > 0)
     {		/* Now we write the Image ID string. */
           //char ImageID[256] = "";		//???
           //char Author[41] = "";
           //char SoftwareID[41] = "";
           //if (fwrite(ImageID,strlen(ImageID),1,f) != 1)
           //{
           //  fclose(f);
           //  return -1;
           //}
       for(int i=0; i<Header.IdentifLen; i++)
           fputc(0,f);
     }

		/* This is where the color map should go if this was not True Color. */
     if(Header.ImageCodeType==TGAColormap)
     {
       for(x=0; x<Header.CMapLength ;x++)
       {
         fputc(CurrImg->Palette->B(x),f);
         fputc(CurrImg->Palette->G(x),f);
         fputc(CurrImg->Palette->R(x),f);
       }
     }

      //if(Header.ImagePixSize != Raster->GetPlanes() Header.ImagePixSize|=0x80;
     y = Header.Height;
     while(y>0)
     {
	y--;
        switch(CurrImg->Raster->GetPlanes())
        {
          case 24:
            {
            char *row = (char *)CurrImg->Raster->GetRow(y);
            RGB_BGR(row,CurrImg->Raster->GetSize1D());
            fwrite(row,3*Header.Width,1,f);
            RGB_BGR(row,CurrImg->Raster->GetSize1D());
            }
            break;

         case 16:
            fwrite(CurrImg->Raster->GetRow(y),2*Header.Width,1,f);
            break;

         case 8:
            fwrite(CurrImg->Raster->GetRow(y),Header.Width,1,f);
            break;

         case 4:
         case 2:
            for(x=0; x<Header.Width; x++)
            {
              uint32_t pix = CurrImg->GetPixel(x,y);
              fputc(pix,f);
            }
            break;

         case 1:
            fwrite(CurrImg->Raster->GetRow(y),(Header.Width+7)/8,1,f);
            break;

         default:		// Reserved for RGB.
            for(x=0; x<Header.Width; x++)
            {
              uint32_t pix = CurrImg->GetPixel(x,y);
              if(CurrImg->Palette)
              {
                fputc(CurrImg->Palette->B(pix),f);
                fputc(CurrImg->Palette->G(pix),f);
                fputc(CurrImg->Palette->R(pix),f);
              }
              else
              {
                fputc(pix,f);  /*b*/ fputc(pix,f);  /*g*/ fputc(pix,f); /*r*/
              }
            }
       }
     }
   }

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

 fclose(f);
 return(0);
}

#endif

#endif
//-------------------End of TGA routines------------------


//-------------------------TXT-plain----------------------
#ifdef SupportTXT


#if SupportTXT>=4 || SupportTXT==2

Image LoadPictureTXT(const char *Name)
{
FILE *f;
uint16_t x,y,R_X;
unsigned char ch;
long max,i;
Raster2DAbstract *Raster=NULL;
Image Img;
Image *CurrImg = &Img;
bool Doubles = false;
long FileStart;

  if((f=fopen(Name,"rt"))==NULL) return(Img);
  FileStart = 0;

  while(!feof(f))
  {
    y = 0;
    max = 0;
    i = 0;
    R_X = 0;

    ch = 0;
    while(!feof(f))	//auto detect sizes and num of planes
    {
      while(!(ch >= '0' && ch <= '9'))
      {             //go to the begin of number
        ch = fgetc(f);
        if(feof(f)) goto EndReading;
        if(ch==0 || ch>128 || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z'))
	  	  goto FINISH; 	//not a textual numeric data
        if(ch=='\n' && y>=1) goto EndReading;
      }
      x = 0;
      do {
        x++;
        if(!isdigit(ch) && ch!=0) ch=0;
        i=ReadInt(f,(char *)&ch);
        if(ch=='.')
        {
          ch = 0;
          if(ReadInt(f,(char *)&ch)!=0)	//dummy read frac part
              Doubles = true;
        }
        if(i>max) max=i;
        if(feof(f)) break;
        while(ch==' ') ch=fgetc(f);	//read spaces to next number
        } while(!(ch==0 || ch==10 || ch==13 || ch==';' || ch==':' || ch>128 ||
	 	 (ch>='a' && ch<='z') || (ch>='A' && ch<='Z')) );

     if(!feof(f))
     {
       if(ch==';') readln(f);		//comment
       if(ch==0 || ch==':' || ch>128 || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z'))
		goto FINISH; 		//not a text data
     }
     if(x>1)
     {
	y++;
	if(x>R_X) R_X=x;
     }
   }
EndReading:
//  if IOResult!=0  goto Konec;
   fseek(f,FileStart,SEEK_SET);

   if(Doubles)
   {
     Raster = CreateRaster2D(R_X,y,-64);
   }
   else
   {
     i=1;
     if(max>=    2) i=2;
     if(max>=    4) i=4;
     if(max>=   16) i=8;
     if(max>=  256) i=16;
     if(max>=65536) i=32;

     Raster = CreateRaster2D(R_X,y,i);
   }

   if(Raster==NULL) goto FINISH;
//if(AlineProc!=nil) AlineProc^.InitPassing(p.y,'Loading TXT');

   y = 0;
   ch = 0;
   while(!feof(f)) 	//load picture data
   {
     while(!(ch >= '0' && ch <= '9'))
     {		//move to the beginning of number
       if(feof(f)) goto RasterFinished;
       ch = fgetc(f);
       if(ch=='\n' && y>=1) goto RasterFinished;
     }

     x = 0;    
     do {
       if(Doubles)
         Raster->SetValue2Dd(x, y, ReadDouble(f,(char *)&ch));
       else
         Raster->SetValue2D(x, y, ReadInt(f,(char *)&ch));
       //if IOresult!=0  goto Konec;
       x++;
       if(ch=='.')
       {
         ch = 0;
         ReadInt(f,(char *)&ch);	//dummy read frac part
       }
       if(feof(f)) break;
       while(ch==' ') ch=fgetc(f);	//read spaces to next number
       } while(!(ch==0 || ch==10 || ch==13 || ch==';' ||  ch>128 || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z')) );

    if(ch==';') readln(f);
    if(x>1) y++;
    }

RasterFinished:
    if(CurrImg->Raster!=NULL)
    {
      CurrImg->Next = new Image;
      CurrImg = CurrImg->Next;
    }
    CurrImg->AttachRaster(Raster);
    Raster = NULL;
    FileStart = ftell(f);
  }

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


#if SupportTXT>=3
int SavePictureTXT(const char *Name, const Image &Img)
{
FILE *f;
uint16_t i,j;
const Image *CurrImg = &Img;

 do
 {
   if(CurrImg->Raster!=NULL && CurrImg->Raster->Size1D>0) break;
   CurrImg = CurrImg->Next;
   if(CurrImg==NULL) return ErrEmptyRaster;
 } while(1);

 if((f=fopen(Name,"wt"))==NULL) return(ErrOpenFile);
 //if AlineProc<>nil AlineProc^.InitPassing(p.y*p.planes,'Saving TXT');

 //printf("1D%d 2D%d",Img.Raster->GetSize1D(),Img.Raster->Size2D);
 while(CurrImg!=NULL)
 {
   for(i=0; i<CurrImg->Raster->Size2D; i++)
   {
     Raster1DAbstract *R1A = CurrImg->Raster->GetRowRaster(i);
     if(R1A)
     {
       if(R1A->GetPlanes()>=0 && R1A->GetPlanes()<=32)
         for(j=0; j<R1A->Size1D; j++)
         {
	   fprintf(f,"%u ",R1A->GetValue1D(j));
         }
       else
         for(j=0;j <R1A->Size1D; j++)
         {
	   fprintf(f,"%f ",R1A->GetValue1Dd(j));
         }
     }
     fprintf(f,"\n");
   }

   CurrImg = CurrImg->Next;
   if(CurrImg!=NULL) fprintf(f,"\n");
   //if AlineProc<>nil then AlineProc^.NextLine;
 }

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

#endif


//-------------------------TXT-gm----------------------
#ifdef SupportTXTgm


#if SupportTXTgm>=4 || SupportTXTgm==2

Image LoadPictureTXTgm(const char *Name)
{
FILE *f;
uint16_t x, y;
unsigned char ch;
long max, i, j;
Raster2DAbstractRGB *Raster=NULL;
Image Img;
Image *CurrImg = &Img;
long RowStart, ImageStart, Image2Start;

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

  Image2Start = ImageStart = 0;
  x=0; y=0;

  do
  {
    if(ImageStart != Image2Start)
    {
      ImageStart = Image2Start;
      fseek(f,ImageStart,SEEK_SET);
    }

  max = 0;
  ch = 0;
  while(!feof(f))	//auto detect sizes and num of planes
    {
    RowStart = ftell(f);
    while(!(ch>='0' && ch<='9'))
	 {             //go to the begin of number
	 ch=fgetc(f);
	 if(feof(f)) goto EndReading;
	 if(ch=='#') {readln(f,(char *)&ch);continue;}  //comment
	 if(ch==0 || ch>128 || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z'))
		goto FINISH; 	//not a text data
	 }
					// x,y: (R,G,B)
   j = ReadInt(f,(char *)&ch);		// x
   if(j>x) x=j;

   while(ch!=',')
   {
     ch=fgetc(f);
     if(ch==10 || ch==13) goto FINISH;
     if(feof(f)) break;
   }
   ch = 0;
   i = ReadInt(f,(char *)&ch);		// y
   if(i>y) y=i;

   if(i==0 && j==0 && RowStart>ImageStart)
   {
     Image2Start = RowStart;
     break;				// Second image frame detected.
   }

   while(ch!=':')
     {ch=fgetc(f);
      if(ch==10 || ch==13) goto FINISH;
      if(feof(f)) break;}
   while(ch!='(')
     {ch=fgetc(f);
      if(ch==10 || ch==13) goto FINISH;
      if(feof(f)) break;}
   ch=0;
   i = ReadInt(f,(char *)&ch);		// R
   if(i>max) max=i;

   while(ch!=',')
     {ch=fgetc(f);
      if(ch==10 || ch==13) goto FINISH;
      if(feof(f)) break;}
   ch=0;
   i=ReadInt(f,(char *)&ch);		//G
   if(i>max) max=i;

   while(ch!=',')
     {ch=fgetc(f);
      if(ch==10 || ch==13) goto FINISH;
      if(feof(f)) break;}
   ch=0;
   i=ReadInt(f,(char *)&ch);		//B
   if(i>max) max=i;

   while(ch!=')')
     {ch=fgetc(f);
      if(ch==10 || ch==13) goto FINISH;
      if(feof(f)) break;}

   readln(f,(char *)&ch);
   }

EndReading:
  i=1;
//  if(max>=    2) i=2;
//  if(max>=    4) i=4;
//  if(max>=   16) i=8;
  if(max>=  256) i=16;
  if(max>=65536) goto FINISH; //!!! Cannot read 48 bits R(16)G(16)B(16)

  Raster = CreateRaster2DRGB(x+1, y+1, (i<=8)?8:16);

  //printf("Creating raster %d %d; max=%d",x,y, max);

  if(Raster==NULL) goto FINISH;
//if(AlineProc!=nil) AlineProc^.InitPassing(p.y,'Loading TXT');

  fseek(f,ImageStart,SEEK_SET);

  while(!feof(f)) 	//load picture data
    {
    x = 0;
    while(!(ch >= '0' && ch <= '9'))
	 {		//move to the beginning of number
	 if(feof(f)) goto RASTER_LOAD;
	 ch = fgetc(f);
	 }
    x = ReadInt(f,(char *)&ch);		// x

    while(ch!=',')
      {ch=fgetc(f);
       if(feof(f)) break;}
    ch = 0;
    y = ReadInt(f,(char *)&ch);		// y

    if(x==0 && y==0 && Image2Start>ImageStart)
    {
      if(ftell(f) >= Image2Start)
	break;	// End of raster reached.
    }

    while(ch!=':')
      {ch=fgetc(f);
       if(feof(f)) break;}
    while(ch!='(')
      {ch=fgetc(f);
       if(feof(f)) break;}
    ch=0;
    i = ReadInt(f,(char *)&ch);		// R
    Raster->setR(x,y,i);

    while(ch!=',')
      {ch=fgetc(f);
       if(feof(f)) break;}
    ch=0;
    i = ReadInt(f,(char *)&ch);		//G
    Raster->setG(x,y,i);

    while(ch!=',')
      {ch=fgetc(f);
       if(feof(f)) break;}
    ch=0;
    i = ReadInt(f,(char *)&ch);		//B
    Raster->setB(x,y,i);

    while(ch!=')')
      {ch=fgetc(f);
       if(feof(f)) break;}

    readln(f,(char *)&ch);
    }

RASTER_LOAD:
  if(CurrImg->Raster != NULL)
    {
      CurrImg->Next = new Image;
      CurrImg = CurrImg->Next;
    }
    CurrImg->AttachRaster(Raster);	Raster=NULL;
    
  } while(Image2Start>ImageStart);

FINISH:
  fclose(f);
   if(Raster) {delete(Raster); Raster=NULL;}
  return(Img);
}
#endif


#if SupportTXTgm>=3
int SavePictureTXTgm(const char *Name, const Image &Img)
{
FILE *f;
uint16_t i, j;
const Image *CurrImg;
APalette *pPalette;
RGBQuad RGB;
char ProcessMode;

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

 if((f=fopen(Name,"wt"))==NULL) return(ErrOpenFile);
 //if AlineProc<>nil AlineProc^.InitPassing(p.y*p.planes,'Saving TXT gm');

 CurrImg = &Img;
 pPalette = NULL;

 do
 {
   if(CurrImg->Raster!=NULL)
   {
     if((CurrImg->ImageType() & 0xFF00) == ImagePalette)
     {
       pPalette = CurrImg->Palette;
       if(pPalette->GetPlanes()/pPalette->Channels() > 8)
         ProcessMode = 2;
       else
         ProcessMode = 1;
     }
     else
     {
       if(CurrImg->Raster->GetPlanes()/CurrImg->Raster->Channels() > 8)
           ProcessMode = 3;		// 48 bit RGB - 16 bit per channel
       else
       {
         if(CurrImg->Raster->GetPlanes()<8)
         {				// Promote less than 256 colors to RGB24
           pPalette = BuildPalette(1<<CurrImg->Raster->GetPlanes());
           if(pPalette==NULL) break;
           FillGray(pPalette);
           ProcessMode = 1;
         }
         else
         {
           if(CurrImg->Raster->Channels() > 3)
               ProcessMode = 5;
           else
               ProcessMode = 4;		// 24 bit RGB - 8 bit per channel
         }
       }
     }

     for(i=0; i<CurrImg->Raster->Size2D; i++)
     {
       const Raster1DAbstract *pRas1D = CurrImg->Raster->GetRowRaster(i);
       switch(ProcessMode)
       {
         case 1:	// Processing throug palette.
                 for(j=0; j<CurrImg->Raster->GetSize1D(); j++)
                 {	  //0,0: (175,160,148) #865F58
                   pPalette->Get(pRas1D->GetValue1D(j),&RGB);
                   fprintf(f,"%u,%u: (%u,%u,%u) #%6.6lX\n", j, i,
			RGB.R, RGB.G, RGB.B, (long)((RGB.R<<16)|(RGB.G<<8)|RGB.B));
                 }
                 break;
         case 2:	// Processing throug 16 bit palette.
                 for(j=0; j<CurrImg->Raster->GetSize1D(); j++)
                 {	  //0,0: (175,160,148) #865F58
                   pPalette->Get(pRas1D->GetValue1D(j), &RGB);
                   fprintf(f,"%u,%u: (%u,%u,%u) #%4.4X%4.4X%4.4X\n", j, i,
			  RGB.R, RGB.G, RGB.B, RGB.R, RGB.G, RGB.B);
                 }
                 break;
         case 3: 	// 16 bit processing.
	         for(j=0; j<pRas1D->GetSize1D(); j++)
                 {	  //0,0: (175,160,148) #86005F005800
                   pRas1D->Get(j,&RGB);
                   fprintf(f,"%u,%u: (%u,%u,%u) #%4.4X%4.4X%4.4X\n", j, i,
			  RGB.R, RGB.G, RGB.B, RGB.R, RGB.G, RGB.B);
                 }
                 break;
        case 4:		// Convert from anything to 24 bit RGB
                 for(j=0; j<pRas1D->GetSize1D(); j++)
                 {	  //0,0: (175,160,148) #865F58
		   pRas1D->Get(j,&RGB);
                   fprintf(f,"%u,%u: (%u,%u,%u) #%6.6lX\n", j, i,
			  RGB.R, RGB.G, RGB.B, (long)((RGB.R<<16)|(RGB.G<<8)|RGB.B));
                 }
                 break;
        case 5:		// Convert from anything to 32 bit RGBA
                 for(j=0; j<pRas1D->GetSize1D(); j++)
                 {	  //0,0: (175,160,148) #865F58
		   pRas1D->Get(j,&RGB);
                   fprintf(f,"%u,%u: (%u,%u,%u,%u) #%6.6lX\n", j, i,
			  RGB.R, RGB.G, RGB.B, RGB.O, (long)((RGB.R<<24)|(RGB.G<<16)|(RGB.B<<8)|RGB.O));
                 }
                 break;
         }
     }

     fprintf(f,"\n");
     //if AlineProc<>nil then AlineProc^.NextLine;
   }

  if(CurrImg->Palette!=pPalette && pPalette!=NULL)
  {
    delete pPalette;
    pPalette = NULL;
  }

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

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

#endif
//-------------------End of TXT routines------------------

//--------------------------WBMP----------------------------
#if SupportWBMP>0

// https://en.wikipedia.org/wiki/Wireless_Application_Protocol_Bitmap_Format

#if SupportWBMP>=4 || SupportWBMP==2

static bool WBMPReadInteger(FILE *f, uint32_t &value)
{
int byte;

  value = 0;
  do
  {
    byte = fgetc(f);
    if(byte==EOF) return false;
    value <<= 7;
    value |= (uint32_t)(byte & 0x7f);
  } while (byte & 0x80);

  return true;
}


Image LoadPictureWBMP(const char *Name)
{
unsigned x, y;
int i;
Image Img;
FILE *f;

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

  if(!WBMPReadInteger(f,x)) goto Finish;
  i = fgetc(f);
  if(i!=0 || x!=0) goto Finish;
  if(!WBMPReadInteger(f,x)) goto Finish;
  if(!WBMPReadInteger(f,y)) goto Finish;
  if(x==0 || y==0) goto Finish;

  Img.AttachRaster(CreateRaster2D(x,y,1));
  if(Img.isEmpty()) goto Finish;

  i = (x+7) / 8;
  for(y=0; y<Img.Raster->Size2D; y++)
  {
    if(fread(Img.Raster->GetRow(y),1,i,f)!=i) break;
  }

Finish:
  if(f) {fclose(f);f=NULL;}
  return Img;
}
#endif


#if SupportWBMP>=3

static void WBMPWriteInteger(FILE *f, uint32_t value)
{
int bits, n;
int i;
uint8_t buffer[5], octet;
bool flag;

  n = 1;
  bits = 28;
  flag = false;
  for(i=4; i>=0; i--)
  {
    octet = ((value >> bits) & 0x7f);
    if(!flag && octet)
    {
      flag = true;
      n = i+1;
    }
    buffer[4-i] = octet | (i && (flag || octet))*(0x01 << 7);
    bits -= 7;
  }
  fwrite(buffer+5-n,1,n,f);
}


int SavePictureWBMP(const char *Name,const Image &Img)
{
FILE *f;
uint16_t ldblk;
uint16_t i;
Raster1D_1Bit TempData;

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

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

  WrWORD_LoEnd(0,f);
  WBMPWriteInteger(f,Img.Raster->Size1D);
  WBMPWriteInteger(f,Img.Raster->Size2D);

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

  if(Img.Raster->GetPlanes()!=1)
  {
    TempData.Allocate1D(Img.Raster->GetSize1D());
  }

  for(i=0; i<Img.Raster->Size2D; i++)
  {
    if(TempData.Data1D)
    {
      TempData.Set(*Img.Raster->GetRowRaster(i));
      fwrite(TempData.Data1D,ldblk,1,f);
    }
    else
      fwrite(Img.Raster->GetRow(i),ldblk,1,f);
  }

  if(TempData.Data1D) TempData.Erase();
  fclose(f);
  return(0);
}
#endif


#endif
//-------------------End of WBMP routines------------------

//--------------------------WPG----------------------------
#if SupportWPG>0
struct WPGHeader
	{
	uint32_t FileId;
	uint32_t DataOffset;
	uint8_t ProductType;
	uint8_t FileType;
	uint8_t MajorVersion;
	uint8_t MinorVersion;
	uint16_t EncryptKey;
	uint16_t Reserved;
	};
//structures for WPG level 1
struct WPGRecord
	{
	uint8_t	RecType;
	uint32_t   RecordLength;
	};

struct WPGCurve
	{
	uint16_t Count;
	uint32_t something;
	int16_t x;
	int16_t y;
	};

struct WPGBitmapType1
	{
	uint16_t Width;
	uint16_t Height;
	uint16_t Depth;
	uint16_t HorzRes;
	uint16_t VertRes;
	};
struct WPGBitmapType2
	{
	uint16_t RotAngle;
	int16_t LowLeftX;
	int16_t LowLeftY;
	int16_t UpRightX;
	int16_t UpRightY;
	uint16_t Width;
	uint16_t Height;
	uint16_t Depth;
	uint16_t HorzRes;
	uint16_t VertRes;
	};
struct WPGColorMapRec
	{
	uint16_t StartIndex;
	uint16_t NumOfEntries;
	};
struct WPGTextL2
	{
	uint8_t Unknown[4];
	uint16_t RotAngle;
	uint8_t Unknown2[2];
	int16_t LowLeftX;
	int16_t LowLeftY;
	int16_t UpRightX;
	int16_t UpRightY;
	uint8_t Unknown3[4];
	};
struct WPGEllipse
	{
	int16_t Sx;
	int16_t Sy;
	uint16_t rx;
	uint16_t ry;
	uint16_t RotAngle;
	uint16_t bAngle;
	uint16_t eAngle;
	uint8_t style;
	};
struct WPGRectangle
	{
	uint16_t X;		// of lower left of rectangle
	uint16_t Y;		// of lower left of rectangle
	int16_t Width; 
	int16_t Height;
	};

struct WPGFigure
	{
	uint32_t Length;	///< Length of object data
	uint16_t RotAngle;	///< Rotation angle from horizontal (degrees)
	int16_t LowLeftX;	///< X value of lower left
	int16_t LowLeftY;	///< Y value of lower left
	int16_t UpRightX;	///< X value of upper right
	int16_t UpRightY; ///< Y value of upper right
	};


//structures for WPG level 2
struct WPG2Record
	{
	uint8_t	Class;
	uint8_t    Type;
	uint32_t   Extension;
	uint32_t   RecordLength;
	};

struct WPG2BitmapData
	{
	uint16_t Width;
	uint16_t Height;
	uint8_t Depth;
	uint8_t Compression;
	};

struct WPG2BitmapRectangle
	{
	//uint16_t Flags;
	int16_t LowLeftX;
	int16_t LowLeftY;
	int16_t UpRightX;
	int16_t UpRightY;
	uint16_t HorzRes;
	uint16_t VertRes;
	};

struct WPG2DblBitmapRectangle
	{
	//uint16_t Flags;
	uint32_t LowLeftX;
	uint32_t LowLeftY;
	uint32_t UpRightX;
	uint32_t UpRightY;
	uint32_t HorzRes;
	uint32_t VertRes;
	};
struct WPG2Start
	{
	uint16_t	HorizontalUnits;
	uint16_t	VerticalUnits;
	uint8_t    PosSizePrecision;
	};
struct WPG2Arc
	{
	//uint16_t Flags;
	int16_t Sx;
	int16_t Sy;
	int16_t rx;
	int16_t ry;
	int16_t Xi,Yi;
	int16_t Xt,Yt;
	uint8_t aFlags;
	};
struct WPG2Rectangle
	{
	//uint16_t Flags;
	int16_t X_ll;
	int16_t Y_ll;
	int16_t X_ur;
	int16_t Y_ur;
	int16_t H_radius;
	int16_t V_radius;
	};
struct WPG2TextLine
	{
	//uint16_t Flags;
	uint16_t TxtFlags;
	int16_t Xref;
	int16_t Yref;
	uint8_t HAlign,VAlign;
	uint32_t Angle;
	};
struct WPG2TextBlock	// Object #1Dh
	{
	//uint16_t Flags;
	uint16_t LowLeftX;
	uint16_t LowLeftY;
	uint16_t UpRightX;
	uint16_t UpRightY;
	};
struct WPG2TextBlockDbl	// Object #1Dh
	{
	//uint16_t Flags;
	uint32_t LowLeftX;
	uint32_t LowLeftY;
	uint32_t UpRightX;
	uint32_t UpRightY;
	};


union WPG_records
	{
	WPGBitmapType1       BitmapType1;
	WPGBitmapType2       BitmapType2;
	WPGColorMapRec       ColorMapRec;
	WPGCurve	     Curve;
	WPGTextL2	     TextL2;
	WPGEllipse	     Ellipse;
	WPGRectangle	     Rectangle;
	WPGFigure	     Figure;

	WPG2BitmapData      _2BitmapData;
	WPG2BitmapRectangle _2BitmapRectangle;
	WPG2DblBitmapRectangle _2DblBitmapRectangle;
	WPG2Arc		    _2Arc;
	WPG2Rectangle	    _2Rect;
	WPG2TextLine	    _2TxtLine;
        WPG2TextBlock       _2TxtBlock;
        WPG2TextBlockDbl    _2TxtBlockDbl;
	};

const RGB_Record WPG2_Palette[256]={
{0,   0,   0},
{0xFF,0xFF,0xFF},
{0x7F,0x7F,0x7F},
{0xBF,0xBF,0xBF},
{0x00,0x00,0x7F},
{0x00,0x7F,0x00},
{0x00,0x7F,0x7F},
{0x7F,0x00,0x00},
{0x7F,0x00,0x7F},
{0x7F,0x7F,0x00},
{0x00,0x00,0xFF},
{0x00,0xFF,0x00},
{0x00,0xFF,0xFF},
{0xFF,0x00,0x00},
{0xFF,0x00,0xFF},
{0xFF,0xFF,0x00}   };


RGB_Record WPG_Palette[256]={
{  0,  0,  0},		{  0,  0,168},  //Original 16 items of palette
{  0,168,  0},		{  0,168,168},
{168,  0,  0},		{168,  0,168},
{168, 84,  0},		{168,168,168},
{ 84, 84, 84},		{ 84, 84,252},
{ 84,252, 84},		{ 84,252,252},
{252, 84, 84},		{252, 84,252},
{252,252, 84},		{252,252,252},  //16

/*
{  0,  0,  0},		{  0,  0,127},	//Strong 16 items of palette
{  0,127,  0},		{  0,127,127},
{127,  0,  0},		{127,  0,127},
{127, 63,  0},		{127,127,127},
{192,192,192},		{  0,  0,255},
{  0,255,  0},		{255,  0,  0},
{255,  0,  0},		{255,  0,255},
{255,255,  0},		{255,255,255},  //16
*/
{  0,  0,  0},		{ 20, 20, 20},
{ 32, 32, 32},		{ 44, 44, 44},
{ 56, 56, 56},		{ 68, 68, 68},
{ 80, 80, 80},		{ 96, 96, 96},
{112,112,112},		{128,128,128},
{144,144,144},		{160,160,160},
{180,180,180},		{200,200,200},
{224,224,224},		{252,252,252},  //32
{  0,  0,252},		{ 64,  0,252},
{124,  0,252},		{188,  0,252},
{252,  0,252},		{252,  0,188},
{252,  0,124},		{252,  0, 64},
{252,  0,  0},		{252, 64,  0},
{252,124,  0},		{252,188,  0},
{252,252,  0},		{188,252,  0},
{124,252,  0},		{ 64,252,  0},	//48
{  0,252,  0},		{  0,252, 64},
{  0,252,124},		{  0,252,188},
{  0,252,252},		{  0,188,252},
{  0,124,252},		{  0, 64,252},
{124,124,252},		{156,124,252},
{188,124,252},		{220,124,252},
{252,124,252},		{252,124,220},
{252,124,188},		{252,124,156},	//64
{252,124,124},		{252,156,124},
{252,188,124},		{252,220,124},
{252,252,124},		{220,252,124},
{188,252,124},		{156,252,124},
{124,252,124},		{124,252,156},
{124,252,188},		{124,252,220},
{124,252,252},		{124,220,252},
{124,188,252},		{124,156,252},	//80
{180,180,252},		{196,180,252},
{216,180,252},		{232,180,252},
{252,180,252},		{252,180,232},
{252,180,216},		{252,180,196},
{252,180,180},		{252,196,180},
{252,216,180},		{252,232,180},
{252,252,180},		{232,252,180},
{216,252,180},		{196,252,180},	//96
{180,220,180},		{180,252,196},
{180,252,216},		{180,252,232},
{180,252,252},		{180,232,252},
{180,216,252},		{180,196,252},
{0,0,112},		{28,0,112},
{56,0,112},		{84,0,112},
{112,0,112},		{112,0,84},
{112,0,56},		{112,0,28},	//112
{112,0,0},		{112,28,0},
{112,56,0},		{112,84,0},
{112,112,0},		{84,112,0},
{56,112,0},		{28,112,0},
{0,112,0},		{0,112,28},
{0,112,56},		{0,112,84},
{0,112,112},		{0,84,112},
{0,56,112},		{0,28,112}, 	//128
{56,56,112},		{68,56,112},
{84,56,112},		{96,56,112},
{112,56,112},		{112,56,96},
{112,56,84},		{112,56,68},
{112,56,56},		{112,68,56},
{112,84,56},		{112,96,56},
{112,112,56},		{96,112,56},
{84,112,56},		{68,112,56},	//144
{56,112,56},		{56,112,69},
{56,112,84},		{56,112,96},
{56,112,112},		{56,96,112},
{56,84,112},		{56,68,112},
{80,80,112},		{88,80,112},
{96,80,112},		{104,80,112},
{112,80,112},		{112,80,104},
{112,80,96},		{112,80,88},	//160
{112,80,80},		{112,88,80},
{112,96,80},		{112,104,80},
{112,112,80},		{104,112,80},
{96,112,80},		{88,112,80},
{80,112,80},		{80,112,88},
{80,112,96},		{80,112,104},
{80,112,112},		{80,114,112},
{80,96,112},		{80,88,112},	//176
{0,0,64},		{16,0,64},
{32,0,64},		{48,0,64},
{64,0,64},		{64,0,48},
{64,0,32},		{64,0,16},
{64,0,0},		{64,16,0},
{64,32,0},		{64,48,0},
{64,64,0},		{48,64,0},
{32,64,0},		{16,64,0},	//192
{0,64,0},		{0,64,16},
{0,64,32},		{0,64,48},
{0,64,64},		{0,48,64},
{0,32,64},		{0,16,64},
{32,32,64},		{40,32,64},
{48,32,64},		{56,32,64},
{64,32,64},		{64,32,56},
{64,32,48},		{64,32,40},	//208
{64,32,32},		{64,40,32},
{64,48,32},		{64,56,32},
{64,64,32},		{56,64,32},
{48,64,32},		{40,64,32},
{32,64,32},		{32,64,40},
{32,64,48},		{32,64,56},
{32,64,64},		{32,56,64},
{32,48,64},		{32,40,64},	//224
{44,44,64},		{48,44,64},
{52,44,64},		{60,44,64},
{64,44,64},		{64,44,60},
{64,44,52},		{64,44,48},
{64,44,44},		{64,48,44},
{64,52,44},		{64,60,44},
{64,64,44},		{60,64,44},
{52,64,44},		{48,64,44},	//240
{44,64,44},		{44,64,48},
{44,64,52},		{44,64,60},
{44,64,64},		{44,60,64},
{44,55,64},		{44,48,64},
{0,0,0},		{0,0,0},
{0,0,0},		{0,0,0},
{0,0,0},		{0,0,0},
{0,0,0},		{0,0,0}		//256
};


inline long LoadWPGHeader(FILE *f, WPGHeader & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,1,sizeof(WPGHeader),f));
#else
return(loadstruct(f,"ddbbbbww",&SU.FileId,&SU.DataOffset,
	&SU.ProductType,&SU.FileType,&SU.MajorVersion,&SU.MinorVersion,&SU.EncryptKey,&SU.Reserved));
#endif
}

inline long LoadWPGBitmapType1(FILE *f,WPGBitmapType1 & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,sizeof(WPGBitmapType1),1,f));
#else
return(loadstruct(f,"wwwww",&SU.Width,&SU.Height,&SU.Depth,&SU.HorzRes,&SU.VertRes));
#endif
}

inline long LoadWPG2BitmapData(FILE *f,WPG2BitmapData & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,sizeof(WPG2BitmapData),1,f));
#else
return(loadstruct(f,"wwbb",&SU.Width,&SU.Height,&SU.Depth,&SU.Compression));
#endif
}

inline long LoadWPG2BitmapRectangle(FILE *f,WPG2BitmapRectangle & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,sizeof(WPG2BitmapRectangle),1,f));
#else
return(loadstruct(f,"wwwwww",&SU.LowLeftX,&SU.LowLeftY,&SU.UpRightX,&SU.UpRightY,
	&SU.HorzRes,&SU.VertRes));
#endif
}

inline long LoadWPG2DblBitmapRectangle(FILE *f,WPG2DblBitmapRectangle & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,sizeof(WPG2DblBitmapRectangle),1,f));
#else
return(loadstruct(f,"dddddd",&SU.LowLeftX,&SU.LowLeftY,&SU.UpRightX,&SU.UpRightY,
	&SU.HorzRes,&SU.VertRes));
#endif
}

inline long LoadWPGBitmapType2(FILE *f, WPGBitmapType2 & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,sizeof(WPGBitmapType2),1,f));
#else
return(loadstruct(f,"wwwwwwwwww",&SU.RotAngle,&SU.LowLeftX,&SU.LowLeftY,&SU.UpRightX,&SU.UpRightY,
	&SU.Width,&SU.Height,&SU.Depth,&SU.HorzRes,&SU.VertRes));
#endif
}

inline long LoadWPGFigure(FILE *f, WPGFigure & SU)
{
#if defined(__PackedStructures__)
return(fread(&SU,sizeof(WPGFigure),1,f));
#else
return(loadstruct(f,"dwwwww",&SU.Length,&SU.RotAngle,&SU.LowLeftX,&SU.LowLeftY,&SU.UpRightX,&SU.UpRightY));
#endif
}

inline long LoadWPGColormap(FILE *f,WPGColorMapRec & SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(WPGColorMapRec),f));
#else
 return(loadstruct(f,"ww",&SU.StartIndex,&SU.NumOfEntries));
#endif
}

inline long LoadWPG2Start(FILE *f,WPG2Start & SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(WPG2Start),f));
#else
 return(loadstruct(f,"wwb",&SU.HorizontalUnits,&SU.VerticalUnits,&SU.PosSizePrecision));
#endif
}

inline long LoadWPG2Rectangle(FILE *f,WPG2Rectangle & SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(WPG2Rectangle),f));
#else
 return(loadstruct(f,"wwwwww",&SU.X_ll,&SU.Y_ll,&SU.X_ur,&SU.Y_ur,
			      &SU.H_radius,&SU.V_radius));
#endif
}

inline long LoadWPG2Arc(FILE *f, WPG2Arc & SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(WPG2Arc),f));
#else
 return(loadstruct(f,"wwwwwwwwb",&SU.Sx,&SU.Sy,&SU.rx,&SU.ry,
				 &SU.Xi,&SU.Yi,&SU.Xt,&SU.Yt,&SU.aFlags));
#endif
}

inline long LoadWPG2TextLine(FILE *f, WPG2TextLine & SU)
{
#if defined(__PackedStructures__)
 return(fread(&SU,1,sizeof(WPG2TextLine),f));
#else
 return(loadstruct(f,"wwwbbd",&SU.TxtFlags,&SU.Xref,&SU.Yref,&SU.HAlign,&SU.VAlign,
			      &SU.Angle));
#endif
}

inline long LoadWPG2TextBlock(FILE *f, WPG2TextBlock & TxtBlk)
{
#if defined(__PackedStructures__)
 return(fread(&TxtBlk,1,sizeof(WPG2TextBlock),f));
#else	
 return(loadstruct(f,"wwww",&TxtBlk.LowLeftX, &TxtBlk.LowLeftY,
                            &TxtBlk.UpRightX,&TxtBlk.UpRightY));
#endif
}

inline long LoadWPG2TextBlockDbl(FILE *f, WPG2TextBlockDbl & TxtBlk)
{
#if defined(__PackedStructures__)
 return(fread(&TxtBlk,1,sizeof(WPG2TextBlock),f));
#else	
 return(loadstruct(f,"dddd",&TxtBlk.LowLeftX, &TxtBlk.LowLeftY,
                            &TxtBlk.UpRightX,&TxtBlk.UpRightY));
#endif
}


#define InsertByte(b) \
{\
 *BObrPos = b;BObrPos++;\
 x++;\
 if(x>=ldblk)\
    {\
    x=0;y++;\
    BObrPos=(uint8_t *)Raster->GetRow(y>=Raster->Size2D?0:y);\
	/*if(AlineProc) AlineProc->NextLine;*/\
    }\
}


#define InsertByte6(b) \
{\
 if(BObrUpPos)\
   {*BObrPos = b ^ *BObrUpPos; BObrUpPos++;}\
 else\
   *BObrPos = b;\
 BObrPos++;\
 x++;\
 if(x>=ldblk)\
    {\
    x=0;y++;\
    BObrPos=(uint8_t *)Raster->GetRow(y>=Raster->Size2D?0:y);\
    BObrUpPos=NULL;\
	/*if(AlineProc) AlineProc->NextLine;*/\
    }\
}


static bool Rd_WP_DWORD(FILE *f, uint32_t *d)
{
   unsigned char b;

   if(fread(&b, sizeof(unsigned char), 1, f) != 1) return false;
   *d = b;
   if(b<0xFF) return true;

   if(fread(&b, sizeof(unsigned char), 1, f) != 1) return false;
   *d = (uint32_t)b ;
   if(fread(&b, sizeof(unsigned char), 1, f) != 1) return false;
   *d += (uint32_t)b * 256;
   if(*d<0x8000) return true;

   *d = (*d & 0x7FFF) << 16;
   if(fread(&b, sizeof(unsigned char), 1, f) != 1) return false;
   *d += (uint32_t)b;
   if(fread(&b, sizeof(unsigned char), 1, f) != 1) return false;
   *d += (uint32_t)b * 256l;
   return true;
}


int UnpackWPGRaster(Raster2DAbstract *Raster,FILE *f)
{
unsigned x,y,i;
uint8_t bbuf,*BObrPos,RunCount;
unsigned long ldblk;

if(Raster==NULL) return(-1);  //Not Enough memory
//	     if AlineProc<>nil then AlineProc^.InitPassing(Raster->Size2D,'Loading WPG');

 x = 0;
 y = 0;
 BObrPos = (uint8_t *)Raster->GetRow(0);
 ldblk = ((long)Raster->GetPlanes()*Raster->GetSize1D()+7)/8;

 while(y<Raster->Size2D)
     {
     if(fread(&bbuf,1,1,f)!=1)
	{
	return(-2);
	}

     RunCount = bbuf & 0x7F;
     if(bbuf & 0x80)
	{
	if(RunCount)	// repeat next byte runcount *
		{
		fread(&bbuf,1,1,f);
		for(i=0;i<RunCount;i++) InsertByte(bbuf);
		}
	   else {	//read next byte as RunCount; repeat 0xFF runcount*
		fread(&RunCount,1,1,f);
		for(i=0;i<RunCount;i++) InsertByte(0xFF);
		}
	}
	else
	{
	if(RunCount)   // next runcount byte are readed directly
		{
		for(i=0;i<RunCount;i++)
			{
			fread(&bbuf,1,1,f);
			InsertByte(bbuf);
			}
		}
	   else {	// repeat previous line runcount*
		fread(&RunCount,1,1,f);
		if(x) {
		      return(-3);  // missing code	  asm int 3; }; { not finished !!!!!!!!}
		      }
		for(i=0;i<RunCount;i++)
			{
			x=0;
			y++;
			if(y>=Raster->Size2D) BObrPos=(uint8_t *)Raster->GetRow(0);
					      BObrPos=(uint8_t *)Raster->GetRow(y);
//			if AlineProc<>nil then AlineProc^.NextLine;
			if(y<2) continue;
			if(y>Raster->Size2D) return(-4);
			memmove(Raster->GetRow(y-1),Raster->GetRow(y-2),ldblk);
			}
                }
        }
   }
 return(0);
}


/** This procedure binds non-gray palette to image. */
void AssignPalette(Image *Img, APalette *Palette)
{
  if(Img==NULL) return;
  Img->AttachPalette(NULL);

  if(Palette==NULL) return;
  if(GrayPalette(Palette,Img->Raster->GetPlanes())) return;

  if(Img->Raster->GetPlanes()==1)   //palete empty check
    {
    if(Palette->GetValue1D(0)==0 && Palette->GetValue1D(1)==0)
           return;    		   //reject crippled monochrome palette
    }

  Img->AttachPalette(Palette);
}


int UnpackWPG2Raster(Raster2DAbstract *Raster,FILE *f)
{
unsigned x,y,i;
uint8_t bbuf,RunCount;
uint8_t *BObrUpPos, *BObrPos;
unsigned long ldblk;
unsigned char SampleBuffer[8];
unsigned char SampleSize = 1;

if(Raster==NULL) return(-1);  //Not Enough memory
//	     if AlineProc<>nil then AlineProc^.InitPassing(Raster->Size2D,'Loading WPG');

 x=0;
 y=0;
 BObrPos = (uint8_t *)Raster->GetRow(0);
 BObrUpPos = NULL;
 ldblk = ((unsigned long)Raster->GetPlanes()*Raster->GetSize1D()+7)/8;

 while(y<Raster->Size2D)
     {
     if(fread(&bbuf,1,1,f)!=1)
	{
	Raster->Erase();
	return(-2);
	}

     switch(bbuf)
       {
       case 0x7D:fread(&SampleSize,1,1,f); 		// DSZ
		 if(SampleSize>8) return(-2);
		 if(SampleSize<1) return(-2);
		 break;
       case 0x7E:if(y==0)				// XOR
		   fprintf(stderr,"\nWPG token XOR on the first line is not supported, please report!");
		 else
		   BObrUpPos = (uint8_t *)Raster->GetRow(y>Raster->Size2D?0:y-1) + x;
		 break;
       case 0x7F:fread(&RunCount,1,1,f);		// BLK
		 for(i=0; i<SampleSize*((unsigned)RunCount+1); i++)
		   InsertByte6(0);
		 break;
       case 0xFD:fread(&RunCount,1,1,f);                                // EXT
		 for(i=0; i<=(unsigned)RunCount; i++)
		    for(bbuf=0; bbuf<SampleSize; bbuf++)
			InsertByte6(SampleBuffer[bbuf]);   //extend previous REP or another EXT token
		 break;
       case 0xFE:fread(&RunCount,1,1,f);				// RST
		 if(x!=0)
			{
			fprintf(stderr,"\nUnsupported WPG2 unaligned token RST x=%u, please report!\n",x);
			return(-3);
			}
		 for(i=0;i<=RunCount;i++)
			{
			x=0;
			y++;
			if(y>=Raster->Size2D) BObrPos=(uint8_t *)Raster->GetRow(0);
					      BObrPos=(uint8_t *)Raster->GetRow(y);
//			if AlineProc<>nil then AlineProc^.NextLine;
			if(y<2) continue;
			if(y>Raster->Size2D) return(-4);
			memmove(Raster->GetRow(y-1),Raster->GetRow(y-2),ldblk);
			}
		 break;
       case 0xFF:fread(&RunCount,1,1,f);		// WHT
		 for(i=0;i<SampleSize*((unsigned)RunCount+1);i++)//read next byte as RunCount; repeat 0xFF runcount*
			{
			InsertByte6(0xFF);
			}
		 break;
       default:
	  RunCount=bbuf & 0x7F;

	  if(bbuf & 0x80)                               //REP
		{	// repeat next byte runcount *
		for(i=0;i<SampleSize;i++)
			fread(&SampleBuffer[i],1,1,f);
		for(i=0;i<=(unsigned)RunCount;i++)
		    for(bbuf=0;bbuf<SampleSize;bbuf++)
			InsertByte6(SampleBuffer[bbuf]);
		}
	   else {                                       //NRP
		for(i=0;i<SampleSize*((unsigned)RunCount+1);i++)
			{
			fread(&bbuf,1,1,f);
			InsertByte6(bbuf);
			}
		}
	  }
   }
 return(0);
}

class WPG2_Transform
{
public:
  typedef enum
  {
    WPG2_TPR = 1,	// Object is to be tapered (perspective).
    WPG2_TRN = 2,       // Object is to be translated.
    WPG2_SKW = 4,       // Object is to be skewed.
    WPG2_SCL = 8,       // Object is to be scaled.
    WPG2_ROT = 0x10,    // Object is to be rotated.
    WPG2_OID = 0x20,	// Object has unique ID.
    WPG2_LCK = 0x80,	// Edit locks are defined for object.
    WPG2_FILL = 0x2000, // Object's interior is to be rendered with current brush.
    WPG2_CLS = 0x4000,	// Object's frame is to be closed.
    WPG2_FRM = 0x8000	// Object is to be framed.
  } WPG2_FLGS;

  WPG2_Transform() {Flags=0;Precision=0;}
  //void ApplyTransform(float &x, float &y);
  void LoadWPG2Flags(FILE *F);

  uint16_t Flags;
  uint8_t Precision;

  float RotAngle;
  float ScaleX;
  float ScaleY;
  float SkewX;
  float SkewY;
  double TranslateX;
  double TranslateY;
  float TamperX;
  float TamperY;

public:
};


/* void WPG2_Transform::ApplyTransform(float &x, float &y)
{
float newX;
float newY;

  if(Flags & WPG2_ROT)
    if(fabs(RotAngle)>1e-6)
      {
      float AngleRad = RotAngle * 180/M_PI;
      newX =  x*cos(AngleRad) + y*sin(AngleRad);
      newY = -x*sin(AngleRad) + y*cos(AngleRad);
      x = newX;
      y = newY;
      }

  if(Flags & WPG2_SCL)
    {
    x *= ScaleX;
    y *= ScaleY;
    }

  if(Flags & WPG2_SKW)
    {
    newX = x + SkewX*y;
    newY = y + SkewY*x;
    x = newX;
    y = newY;
    }

  if(Flags & WPG2_TRN)
    {
    x += TranslateX;
    y += TranslateY;
    }

  if(Flags & WPG2_TPR)
    {
    newX = x + TamperX*y;
    newY = y + TamperY*x;
    x = newX;
    y = newY;
    }
}
*/


void WPG2_Transform::LoadWPG2Flags(FILE *F)
{
int32_t x;
uint16_t xW, DenX;

 Rd_word(F,&Flags);
 //fprintf(DEBUG_CTM,"Flags=%X",Flags);
 if(Flags & WPG2_LCK) Rd_dword(F,(uint32_t *)&x);	//Edit lock
 if(Flags & WPG2_OID)
	{
	if(Precision==0)
	  {
	  Rd_word(F,&xW);
	  if(xW>=0x8000)
	    {
	    Precision = 1;	// Double precision Switched on.
	    Rd_word(F,&DenX);
	    x = ((xW & 0x7FFF)<<16) | DenX;
	    }
	  else x=xW;
	  }		//ObjectID
	else
	  {Rd_dword(F,(uint32_t *)&x);}		//ObjectID (Double precision)
	//fprintf(DEBUG_CTM,"; OID=%X",x);
	}
 if(Flags & WPG2_ROT)
	{
	Rd_dword(F,(uint32_t *)&x);		//Rot Angle
	RotAngle = x/65536.0;
        //fprintf(DEBUG_CTM,"; RotAngle=%X,%.2f",x,RotAngle);
	}
 else  RotAngle = 0;

 if(Flags & (WPG2_SCL | WPG2_ROT))	//Note: rotation forces SCL to be inserted.
	{
	Rd_dword(F,(uint32_t *)&x);		//Sx*cos()
	ScaleX = (float)x/0x10000;
        //fprintf(DEBUG_CTM,"; Sclx=%X,%.2f",x,ScaleX);
	Rd_dword(F,(uint32_t *)&x);		//Sy*cos()
	ScaleY = (float)x/0x10000;
        //fprintf(DEBUG_CTM,"; Scly=%X,%.2f",x,ScaleY);
	}
 else ScaleX = ScaleY = 1;

 if(Flags & (WPG2_SKW | WPG2_ROT))	//Note: rotation forces SKW to be inserted.
	{
	Rd_dword(F,(uint32_t *)&x);		//Kx*sin()
	SkewX = (float)x/0x10000;
	//fprintf(DEBUG_CTM,"; SkewX=%X,%.2f",x,SkewX);
	Rd_dword(F,(uint32_t *)&x);		//Ky*sin()
	SkewY = (float)x/0x10000;
        //fprintf(DEBUG_CTM,"; SkewY=%X,%.2f",x,SkewY);
	}
 else SkewX = SkewY = 0;

 if(Flags & WPG2_TRN)
	{
	Rd_word(F,&DenX); Rd_dword(F,(uint32_t*)&x);	//Tx
	TranslateX = (double)x + (float)DenX/0x10000;
        //fprintf(DEBUG_CTM,"; TranslateX=%X/%X,%.2f",x,DenX,TranslateX);
	Rd_word(F,&DenX); Rd_dword(F,(uint32_t*)&x);	//Ty
	TranslateY = (double)x + (float)DenX/0x10000;
	//fprintf(DEBUG_CTM,"; TranslateY=%X/%X,%.2f",x,DenX,TranslateY);
	}
 else TranslateX = TranslateY = 0;

 if(Flags & WPG2_TPR)
	{
	Rd_word(F,&xW);	Rd_word(F,&DenX);	//Px
	TamperX = xW + (float)DenX/0x10000;
        //fprintf(DEBUG_CTM,"; TamperX=%X,%.2f",x,TamperX);
	Rd_word(F,&xW); Rd_word(F,&DenX);	//Py
	TamperY = xW + (float)DenX/0x10000;
        //fprintf(DEBUG_CTM,"; TamperY=%X,%.2f",x,TamperY);
	}
 else TamperX = TamperY = 0;
}


#if SupportWPG>=4 || SupportWPG==2
Image LoadPictureWPG(const char *Name)
{
FILE *f;
long len,ldblk;
unsigned i;
WPGHeader  Header;
WPGRecord  Rec;
WPG2Record  Rec2;
WPG_records WPG;
WPG2Start StartWPG;
WPGColorMapRec wpPal;
Raster2DAbstract *Raster=NULL;
APalette *Palette=NULL;
Image Img,*CurrImg;
float RotAngle,x,y,dx,dy;

 CurrImg=&Img;

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

 len = LoadWPGHeader(f,Header);
 if(Header.FileId!=0x435057FF || len!=16 ||
    Header.FileType!=0x16 || Header.EncryptKey!=0)
	 {fclose(f);return(NULL);}

 RotAngle=x=y=dx=dy=0;
 switch(Header.MajorVersion)
   {
   case 1:
     while(!feof(f))
       {
       fseek(f,Header.DataOffset,SEEK_SET);
       if(feof(f)) break;

       Rec.RecType=fgetc(f);
       if(!Rd_WP_DWORD(f,&Rec.RecordLength)) break;

       Header.DataOffset = ftell(f)+Rec.RecordLength;

       //!!!!!!The internal palette should be loaded right here

       switch(Rec.RecType)
	 {
	 case 0x0B:	// bitmap type 1
		 LoadWPGBitmapType1(f,WPG.BitmapType1);
		 if(Raster!=NULL) break;
		 Raster=CreateRaster2D(WPG.BitmapType1.Width,WPG.BitmapType1.Height,WPG.BitmapType1.Depth);
		 if(Raster)
		   {
		   if(UnpackWPGRaster(Raster,f)<0) Raster->Erase();
/*		   i = ftell(f);
		   if(i!=Header.DataOffset) 
			printf("mismatch position"); */
LoadRaster:	   if(Img.Raster!=NULL)
		     {
		     CurrImg->Next=new Image;
		     CurrImg=CurrImg->Next;
		     }
		   RotAngle-=360*((long)RotAngle/360);
		   CurrImg->x=x;
		   CurrImg->y=y;
		   CurrImg->dx=dx;
		   CurrImg->dy=dy;
		   CurrImg->RotAngle=RotAngle;
		   RotAngle=x=y=dx=dy=0;

		   CurrImg->AttachRaster(Raster);		   
		   if(Raster->GetPlanes()>1)		// Assign palette for non monochrome images.
		     AssignPalette(CurrImg,Palette);
		   Raster=NULL;
		   }
		 break;
	 case 0x0E:	// color palette
		 LoadWPGColormap(f,wpPal);
		 if(Palette!=NULL && Palette->UsageCount==0) delete Palette;
		 Palette = BuildPalette(wpPal.NumOfEntries+wpPal.StartIndex,8);
		 if(Palette!=NULL)
		    {
		    fread((char *)Palette->Data1D + 3*wpPal.StartIndex, 3*wpPal.NumOfEntries,1,f);
		    }
		 break;

	 case 0x14:	// bitmap type 2
		 LoadWPGBitmapType2(f,WPG.BitmapType2);
		 if(Raster!=NULL) break;
		 Raster=CreateRaster2D(WPG.BitmapType2.Width,WPG.BitmapType2.Height,WPG.BitmapType2.Depth);
		 if(Raster==NULL) break;
		 x = WPG.BitmapType2.LowLeftX/47.0;
		 y = WPG.BitmapType2.LowLeftY/47.0;
		 dx= (WPG.BitmapType2.UpRightX-WPG.BitmapType2.LowLeftX)/47.0;
		 dy= (WPG.BitmapType2.UpRightY-WPG.BitmapType2.LowLeftY)/47.0;
		 RotAngle=WPG.BitmapType2.RotAngle & 0x0FFF;
		 if(UnpackWPGRaster(Raster,f)<0)
					{delete Raster;Raster=NULL;break;}
		 if(WPG.BitmapType2.RotAngle & 0x8000)
				   {Flip1D(Raster);RotAngle=360-RotAngle;}
		 if(WPG.BitmapType2.RotAngle & 0x2000)
				   {
				   Flip2D(Raster);
				   if((WPG.BitmapType2.RotAngle & 0x8000)==0)
					RotAngle=360-RotAngle;
				   }
		 goto LoadRaster;
	 }
      }
     break;
   case 2:
     float_matrix CTM(3,3);
     StartWPG.PosSizePrecision=0;
     while(!feof(f))
       {
       fseek(f,Header.DataOffset,SEEK_SET);
       if(feof(f)) break;

       Rec2.Class=fgetc(f);
       Rec2.Type=fgetc(f);
       Rd_WP_DWORD(f,&Rec2.Extension);
       Rd_WP_DWORD(f,&Rec2.RecordLength);

       Header.DataOffset=ftell(f)+Rec2.RecordLength;

       switch(Rec2.Type)
	 {
	 case 0x01:LoadWPG2Start(f,StartWPG);
		   break;
	 case 0x0C:	// color palette
		 LoadWPGColormap(f,wpPal);
		 if(Palette!=NULL && Palette->UsageCount==0) delete Palette;
		 Palette=BuildPalette(wpPal.NumOfEntries+wpPal.StartIndex,8);
		 if(Palette!=NULL)
		    {
		    for(i=wpPal.StartIndex;i<wpPal.NumOfEntries;i++)
			{
			Palette->setR(i,fgetc(f));
			Palette->setG(i,fgetc(f));
			Palette->setB(i,fgetc(f));
			fgetc(f);
			}
		    }
		 break;

	 case 0x0E:	//raster data
	     LoadWPG2BitmapData(f,WPG._2BitmapData);
	     if(WPG._2BitmapData.Compression>1) goto Skip_WP_BMP;
	     switch(WPG._2BitmapData.Depth)
	       {
	       case 1:i=1;break;
	       case 2:i=2;break;
	       case 3:i=4;break;
	       case 4:i=8;break;
	       case 8:i=24;break;
	       default:continue;  /*Ignore raster with unknown depth*/
	       }
	     Raster=CreateRaster2D(WPG._2BitmapData.Width,WPG._2BitmapData.Height,i);
	     if(Raster==NULL) continue;

	     if(WPG._2BitmapData.Compression==1)
		   {
		   if(UnpackWPG2Raster(Raster,f)<0)
				  Raster->Erase();
		   }
	     if(WPG._2BitmapData.Compression==0)
		{
		ldblk=(Raster->GetPlanes()*Raster->GetSize1D()+7)/8;
		for(i=0;i<Raster->Size2D;i++)
		   {
		   if(fread(Raster->GetRow(i),ldblk,1,f)!=1) {goto FINISH;}
	//	   AlineProc(i,p);
		   }
		}
	     if(Img.Raster!=NULL)
		   {
		   CurrImg->Next=new Image;
		   CurrImg=CurrImg->Next;
		   }
	     CurrImg->x=x;
	     CurrImg->y=y;
	     CurrImg->dx=dx;
	     CurrImg->dy=dy;
	     x=y=dx=dy=0;
             if(Raster!=NULL)
		{
		if(CTM.member(0,0)<0)
		  {
		  Flip1D(Raster); //?? RotAngle=360-RotAngle;
			/* Try to change CTM according to Flip - I am not sure, must be checked. */
		  float_matrix Tx(3,3);
		  Tx(0,0)=-1;      Tx(1,0)=0;   Tx(2,0)=0;
		  Tx(0,1)= 0;      Tx(1,1)=1;   Tx(2,1)=0;
		  Tx(0,2)=(WPG._2Rect.X_ur+WPG._2Rect.X_ll);
		                   Tx(1,2)=0;   Tx(2,2)=1;
                  CTM *= Tx;
                  }
		if(CTM.member(1,1)<0)
		  {
		  Flip2D(Raster); //?? RotAngle=360-RotAngle;
			  /* Try to change CTM according to Flip - I am not sure, must be checked. */
		  float_matrix Tx(3,3);
		  Tx(0,0)= 1;   Tx(1,0)= 0;   Tx(2,0)=0;
		  Tx(0,1)= 0;   Tx(1,1)=-1;   Tx(2,1)=0;
		  Tx(0,2)= 0;   Tx(1,2)=(WPG._2Rect.Y_ur+WPG._2Rect.Y_ll);
					      Tx(2,2)=1;
		  CTM *= Tx;
		  }
		CurrImg->AttachRaster(Raster);
		Raster=NULL;
		}
	     AssignPalette(CurrImg,Palette);
	     break;
	 case 0x1B:	//bitmap rectangle
             WPG2_Transform TRx;
	     TRx.Precision = StartWPG.PosSizePrecision;
	     TRx.LoadWPG2Flags(f);
             RotAngle = TRx.RotAngle;

	     if(StartWPG.PosSizePrecision==0)
			{
			LoadWPG2BitmapRectangle(f,WPG._2BitmapRectangle);
			x=WPG._2BitmapRectangle.LowLeftX/47.0;
			y=WPG._2BitmapRectangle.LowLeftY/47.0;
			dx=(WPG._2BitmapRectangle.UpRightX-WPG._2BitmapRectangle.LowLeftX)/47.0;
			dy=(WPG._2BitmapRectangle.UpRightY-WPG._2BitmapRectangle.LowLeftY)/47.0;
			break;
			}
	     if(StartWPG.PosSizePrecision==1)
			{
			LoadWPG2DblBitmapRectangle(f,WPG._2DblBitmapRectangle);
			x=WPG._2DblBitmapRectangle.LowLeftX/(47.0*0xFFFF);
			y=WPG._2DblBitmapRectangle.LowLeftY/(47.0*0xFFFF);
			dx=(WPG._2DblBitmapRectangle.UpRightX-WPG._2DblBitmapRectangle.LowLeftX)/(47.0*0xFFFF);
			dy=(WPG._2DblBitmapRectangle.UpRightY-WPG._2DblBitmapRectangle.LowLeftY)/(47.0*0xFFFF);
			break;

		}
	     break;
	 }
Skip_WP_BMP: {}
       }
     break;
   }

FINISH:
 if(f) fclose(f);
 if(Palette!=NULL && Palette->UsageCount==0) delete Palette;
 return(Img);
}
#endif


#if SupportWPG>=3

class WPG_RLE_packer
{
public:	WPG_RLE_packer() {pos=count=0;}
	void AddCharacter(uint8_t b, FILE *F);
	void AddBlock(const uint8_t *blk, uint16_t size, FILE *F);
	void Flush(FILE *F, uint8_t n);
	void Flush(FILE *F);
	uint8_t count;
	uint8_t pos;
	uint8_t buf[254];
};


void WPG_RLE_packer::AddCharacter(uint8_t b, FILE *F)
{
  buf[pos++] = b;

  if(pos>1)
  {
    if(count==0x7E || buf[pos-2]!=b)
    {
      if(count>=1)
      {
        count++;		// True number of repeated uint8_ts.
        Flush(F,pos-1-count);
        fputc(count|0x80,F);
        fputc(buf[0],F);
        pos = 1;
        buf[0] = b;
      }
      count = 0;
    }
    else
      count++;
  }

  if(pos-count>0x7E)	// We have uncompressible block with size 0x7F.
  {
    Flush(F, 0x7F);
    return;
  }
  if(pos>0x7E && count>=1)
  {
    Flush(F, pos-1-count);
    return;
  }
}


void WPG_RLE_packer::AddBlock(const uint8_t *blk, uint16_t size, FILE *F)
{
  while(size-- > 0)
  {
    AddCharacter(*blk,F);
    blk++;
  }
}


void WPG_RLE_packer::Flush(FILE *F, uint8_t n)
{
  if(n>pos) n=pos;
  if(n>0x7F) n=0x7F;
  if(n>0)
  {
    fputc(n,F);
    fwrite(buf,1,n,F);
    pos -= n;
    if(pos>0)
      memcpy(buf, buf+n, n);
    else
     count = 0;
  }
}


void WPG_RLE_packer::Flush(FILE *F)
{
  if(count>1)
  {
    AddCharacter(buf[pos-1]^0xFF,F); // Add fake uint8_t.
    pos = 0;			// Take a last fake uint8_t away.
  }
  else
  {
    Flush(F,0x7F);
    Flush(F,0x7F);
    count = 0;
  }
}


int SavePictureWPG(const char *Name,const Image &Img)
{
FILE *f;
uint16_t ldblk;
uint16_t i;
char StoredPlanes;
Raster1D_1Bit TempData;
uint32_t NumericOffs, CurrOffs;

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

  switch(Img.Raster->GetPlanes())
  {
    case 1: StoredPlanes=1;
            ldblk = (Img.Raster->GetSize1D()+7)/8;
            break;
    case 2:
    case 4: StoredPlanes=4;
            ldblk = (Img.Raster->GetSize1D()+1)/2;
            break;
    default:
    case 8: StoredPlanes=8;
	    ldblk = Img.Raster->GetSize1D();
  }

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

	// Header
  WrDWORD_LoEnd(0x435057FF,f);		//DWORD FileId
  WrDWORD_LoEnd(16,f);			//DWORD DataOffset;
  fputc(1,f);				//uint8_t Product Type
  fputc(0x16,f);			//uint8_t FileType;
  fputc(1,f);				//uint8_t MajorVersion;
  fputc(0,f);				//uint8_t MinorVersion;
  WrWORD_LoEnd(0,f);			//WORD EncryptKey;
  WrWORD_LoEnd(0,f);			//WORD Reserved;

	// Start WPG l1
  fputc(0xF,f);
  fputc(0x6,f);
  fputc(1,f);					//uint8_t Version number
  fputc(0,f);					//uint8_t Flags (bit 0 PostScript, maybe bitmap, bit 1 PostScript, no bitmap
  WrWORD_LoEnd(Img.Raster->GetSize1D(),f);	//WORD Width of image (arbitrary units)
  WrWORD_LoEnd(Img.Raster->GetSize2D(),f);	//WORD Height of image (arbitrary units)

	// Palette
  if(StoredPlanes > 1)
  {
    APalette *Palette = Img.Palette;
    if(Palette==NULL)
    {
      Palette = BuildPalette(1<<StoredPlanes,8);
      FillGray(Palette);		// NULL tolerant.
    }
    if(Palette!=NULL)
    {
      fputc(0xE,f);
      i = 4+3*(1<<StoredPlanes);
      if(i<0xFF)
          fputc(i,f);
      else
      {
        fputc(0xFF,f);
        WrWORD_LoEnd(i, f);
      }
      WrWORD_LoEnd(0,f);				// Start index
      WrWORD_LoEnd((1<<StoredPlanes), f);		// Num entries

      uint16_t k = Palette->GetPlanes()/3-8;
      if(k<0) k=0;
      for(i=0; i<(1 << StoredPlanes); i++)
      {
        fputc(Palette->R(i)>>k,f);
        fputc(Palette->G(i)>>k,f);
        fputc(Palette->B(i)>>k,f);
      }
      if(Palette!=Img.Palette) delete Palette;
    }
  }

	// Bitmap 1 header
  fputc(0xB,f);
  fputc(0xFF,f);
  NumericOffs = ftell(f);
  WrWORD_LoEnd(0x8000,f);
  WrWORD_LoEnd(0,f);

  WrWORD_LoEnd(Img.Raster->GetSize1D(),f);	//WORD Width
  WrWORD_LoEnd(Img.Raster->GetSize2D(),f);	//WORD Height
  WrWORD_LoEnd(StoredPlanes,f);			//WORD Depth;
  WrWORD_LoEnd(75,f);				//WORD HorzRes;
  WrWORD_LoEnd(75,f);				//WORD VertRes;

  if(Img.Raster->GetPlanes()!=StoredPlanes)
    {
    TempData.Allocate1D(Img.Raster->GetSize1D());
    }

  WPG_RLE_packer PackRLE;
  for(i=0; i<Img.Raster->Size2D; i++)
	{
	if(TempData.Data1D)
	  {
	  TempData.Set(*Img.Raster->GetRowRaster(i));
	  PackRLE.AddBlock((const uint8_t*)TempData.Data1D,ldblk,f);
	  }
	else
	  PackRLE.AddBlock((const uint8_t*)Img.Raster->GetRow(i),ldblk,f);
	PackRLE.Flush(f);
	}
  if(TempData.Data1D) TempData.Erase();

  CurrOffs = ftell(f);
  fseek(f,NumericOffs,SEEK_SET);
  NumericOffs = CurrOffs - NumericOffs - 4;
  WrWORD_LoEnd(0x8000|(NumericOffs>>16),f);
  WrWORD_LoEnd(NumericOffs&0xFFFF,f);
  fseek(f,CurrOffs,SEEK_SET);

	// End of WPG data
  fputc(0x10,f);
  fputc(0,f);

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

#endif //SupportWPG
//-------------------End of WPG routines------------------


//------------------XPM2--------------------------
#ifdef SupportXPM

// See for description: https://en.wikipedia.org/wiki/X_PixMap

typedef struct
{
  const char *Name;
  uint32_t RGB;
} X11ColorName;

// https://en.wikipedia.org/wiki/X11_color_names
const X11ColorName X11ColorNames[] =
{
 {"Alice Blue",0xF0F8FF},
 {"Antique White",0xFAEBD7},
 {"Aqua",0x00FFFF},
 {"Aquamarine",0x7FFFD4},
 {"Azure",0xF0FFFF},
 {"Beige",0xF5F5DC},
 {"Bisque",0xFFE4C4},
 {"Black",0x000000},
 {"Blanched Almond",0xFFEBCD},
 {"Blue",0x0000FF},
 {"Blue Violet",0x8A2BE2},
 {"Brown",0xA52A2A},
 {"Burlywood",0xDEB887},
 {"Cadet Blue",0x5F9EA0},
 {"Chartreuse",0x7FFF00},
 {"Chocolate",0xD2691E},
 {"Coral",0xFF7F50},
 {"Cornflower Blue",0x6495ED},
 {"Cornsilk",0xFFF8DC},
 {"Crimson",0xDC143C},
 {"Cyan",0x00FFFF},
 {"Dark Blue",0x00008B},
 {"Dark Cyan",0x008B8B},
 {"Dark Goldenrod",0xB8860B},
 {"Dark Gray",0xA9A9A9},
 {"Dark Green",0x006400},
 {"Dark Khaki",0xBDB76B},
 {"Dark Magenta",0x8B008B},
 {"Dark Olive Green",0x556B2F},
 {"Dark Orange",0xFF8C00},
 {"Dark Orchid",0x9932CC},
 {"Dark Red",0x8B0000},
 {"Dark Salmon",0xE9967A},
 {"Dark Sea Green",0x8FBC8F},
 {"Dark Slate Blue",0x483D8B},
 {"Dark Slate Gray",0x2F4F4F},
 {"Dark Turquoise",0x00CED1},
 {"Dark Violet",0x9400D3},
 {"Deep Pink",0xFF1493},
 {"Deep Sky Blue",0x00BFFF},
 {"Dim Gray",0x696969},
 {"Dodger Blue",0x1E90FF},
 {"Firebrick",0xB22222},
 {"Floral White",0xFFFAF0},
 {"Forest Green",0x228B22},
 {"Fuchsia",0xFF00FF},
 {"Gainsboro*",0xDCDCDC},
 {"Ghost White",0xF8F8FF},
 {"Gold",0xFFD700},
 {"Goldenrod",0xDAA520},
 {"Gray",0xBEBEBE},
 {"Web Gray",0x808080},
 {"Green",0x00FF00},
 {"Web Green",0x008000},
 {"Green Yellow",0xADFF2F},
 {"Honeydew",0xF0FFF0},
 {"Hot Pink",0xFF69B4},
 {"Indian Red",0xCD5C5C},
 {"Indigo",0x4B0082},
 {"Ivory",0xFFFFF0},
 {"Khaki",0xF0E68C},
 {"Lavender",0xE6E6FA},
 {"Lavender Blush",0xFFF0F5},
 {"Lawn Green",0x7CFC00},
 {"Lemon Chiffon",0xFFFACD},
 {"Light Blue",0xADD8E6},
 {"Light Coral",0xF08080},
 {"Light Cyan",0xE0FFFF},
 {"Light Goldenrod",0xFAFAD2},
 {"Light Gray",0xD3D3D3},
 {"Light Green",0x90EE90},
 {"Light Pink",0xFFB6C1},
 {"Light Salmon",0xFFA07A},
 {"Light Sea Green",0x20B2AA},
 {"Light Sky Blue",0x87CEFA},
 {"Light Slate Gray",0x778899},
 {"Light Steel Blue",0xB0C4DE},
 {"Light Yellow",0xFFFFE0},
 {"Lime",0x00FF00},
 {"Lime Green",0x32CD32},
 {"Linen",0xFAF0E6},
 {"Magenta",0xFF00FF},
 {"Maroon",0xB03060},
 {"Web Maroon",0x800000},
 {"Medium Aquamarine",0x66CDAA},
 {"Medium Blue",0x0000CD},
 {"Medium Orchid",0xBA55D3},
 {"Medium Purple",0x9370DB},
 {"Medium Sea Green",0x3CB371},
 {"Medium Slate Blue",0x7B68EE},
 {"Medium Spring Green",0x00FA9A},
 {"Medium Turquoise",0x48D1CC},
 {"Medium Violet Red",0xC71585},
 {"Midnight Blue",0x191970},
 {"Mint Cream",0xF5FFFA},
 {"Misty Rose",0xFFE4E1},
 {"Moccasin",0xFFE4B5},
 {"Navajo White",0xFFDEAD},
 {"Navy Blue",0x000080},
 {"Old Lace",0xFDF5E6},
 {"Olive",0x808000},
 {"Olive Drab",0x6B8E23},
 {"Orange",0xFFA500},
 {"Orange Red",0xFF4500},
 {"Orchid",0xDA70D6},
 {"Pale Goldenrod",0xEEE8AA},
 {"Pale Green",0x98FB98},
 {"Pale Turquoise",0xAFEEEE},
 {"Pale Violet Red",0xDB7093},
 {"Papaya Whip",0xFFEFD5},
 {"Peach Puff",0xFFDAB9},
 {"Peru",0xCD853F},
 {"Pink",0xFFC0CB},
 {"Plum",0xDDA0DD},
 {"Powder Blue",0xB0E0E6},
 {"Purple",0xA020F0},
 {"Web Purple",0x800080},
 {"Rebecca Purple",0x663399},
 {"Red",0xFF0000},
 {"Rosy Brown",0xBC8F8F},
 {"Royal Blue",0x4169E1},
 {"Saddle Brown",0x8B4513},
 {"Salmon",0xFA8072},
 {"Sandy Brown",0xF4A460},
 {"Sea Green",0x2E8B57},
 {"Seashell",0xFFF5EE},
 {"Sienna",0xA0522D},
 {"Silver",0xC0C0C0},
 {"Sky Blue",0x87CEEB},
 {"Slate Blue",0x6A5ACD},
 {"Slate Gray",0x708090},
 {"Snow",0xFFFAFA},
 {"Spring Green",0x00FF7F},
 {"Steel Blue",0x4682B4},
 {"Tan",0xD2B48C},
 {"Teal",0x008080},
 {"Thistle",0xD8BFD8},
 {"Tomato",0xFF6347},
 {"Turquoise",0x40E0D0},
 {"Violet",0xEE82EE},
 {"Wheat",0xF5DEB3},
 {"White",0xFFFFFF},
 {"White Smoke",0xF5F5F5},
 {"Yellow",0xFFFF00},
 {"Yellow Green",0x9ACD32},

 {"None",0x000000}
};


#if SupportXPM>=4 || SupportXPM==2

/** Decode one color string record.
 * @return 0-Invalid color, 1-8bit color, 2-16bit color, 3-symbolic color (8bit) */
int DecodeColorXPM(char *chLine, RGBQuad &RGB)
{
  do
    {chLine++;}
  while(isspace(*chLine));

  if((*chLine=='s' || *chLine=='c') && isspace(chLine[1]))
  {
    chLine += 2;
    while(isspace(*chLine)) chLine++;
  }
   //printf("\n%d%s",i,chLine);

  if(*chLine==0) return 0;
  if(*chLine=='#')
  {
    chLine++;		// toss away '#'
    size_t j = strlen(chLine);
    if(j>=12 && isxdigit(chLine[6]))		// 16 bit palette depth.
    {							// @TODO: add support for 16bit palette, currently schrinked to 8 bits.
      char ch = chLine[4]; chLine[4]=0;
      RGB.R = strtoul(chLine, NULL, 16);
      chLine += 4;
      chLine[0] = ch;

      ch = chLine[4]; chLine[4]=0;
      RGB.G = strtoul(chLine, NULL, 16);
      chLine += 4;
      chLine[0] = ch;

      ch = chLine[4]; chLine[4]=0;
      RGB.B = strtoul(chLine, NULL, 16);
      chLine[4] = ch;
      RGB.O = 0;
      return 2;
    }
    if(j>=6)
    {
      RGB.R = 16*((*chLine<='9') ? (*chLine-'0') : (*chLine-'A'+10));
      chLine++;
      RGB.R |= (*chLine<='9') ? (*chLine-'0') : (*chLine-'A'+10);
      chLine++;
      RGB.G = 16*((*chLine<='9') ? (*chLine-'0') : (*chLine-'A'+10));
      chLine++;
      RGB.G |= (*chLine<='9') ? (*chLine-'0') : (*chLine-'A'+10);
      chLine++;
      RGB.B = 16*((*chLine<='9') ? (*chLine-'0') : (*chLine-'A'+10));
      chLine++;
      RGB.B |= (*chLine<='9') ? (*chLine-'0') : (*chLine-'A'+10);
      RGB.O = 0;
      return 1;
    }
  }
  else
  {
    for(unsigned j=0; j<sizeof(X11ColorNames)/sizeof(X11ColorName); j++)
    {
      if(!stricmp(X11ColorNames[j].Name,chLine))
      {
        RGB.B = X11ColorNames[j].RGB & 0xFF;
        RGB.G = (X11ColorNames[j].RGB>>8) & 0xFF;
        RGB.R = (X11ColorNames[j].RGB>>16) & 0xFF;
        RGB.O = 0;
        return 3;
      }
    }
  }
  return 0;
}


Image LoadPictureXPM2(const char *Name)
{
FILE  *f;
Raster2DAbstract *Raster = NULL;
APalette *pPalette = NULL;
Image Img;
string Line;
char *chLine;
char *DecodingArea = NULL;
unsigned i;
int j;
uint16_t Width, Height;
uint16_t X,Y;
uint16_t nColors, CharsPerPixel;
uint8_t Planes;

 if((f=fopen(Name,"rb"))==NULL) return(Img);
 fGets2(f,Line,80);
 if(Line=="! XPM2")
     fGets2(f,Line,80);
 if(Line.isEmpty()) goto ENDPROC;

 for(i=0; i<Line.length(); i++)
 {
   const unsigned char c = Line[i];
   if(!isspace(c) && !isdigit(c))  goto ENDPROC;	// Image is invalid.
 }
 chLine = Line();
 Width = atoi(chLine);
 if(Width==0) goto ENDPROC;
 while(isdigit(*chLine)) chLine++;
 while(isspace(*chLine)) chLine++;
 if(*chLine==0) goto ENDPROC;
 Height = atoi(chLine);
 if(Height==0) goto ENDPROC;
 while(isdigit(*chLine)) chLine++;
 while(isspace(*chLine)) chLine++;
 if(*chLine==0) goto ENDPROC;
 nColors = atoi(chLine);
 while(isdigit(*chLine)) chLine++;
 while(isspace(*chLine)) chLine++;
 if(*chLine==0) goto ENDPROC;
 CharsPerPixel = atoi(chLine);
 if(CharsPerPixel > 2) goto ENDPROC;	// Not supported.
 chLine = NULL;

 if(nColors<=2) Planes=1;
 else if(nColors<=4) Planes=2;
 else if(nColors<=16) Planes=4;
 else if(nColors<=256) Planes=8;
 else goto ENDPROC;

 Raster = CreateRaster2D(Width,Height,Planes);
 if(Raster==NULL) goto ENDPROC;

 pPalette = BuildPalette(nColors, 8);
 if(pPalette==NULL) goto ENDPROC;

 DecodingArea = (char*)calloc(nColors+1,CharsPerPixel);

 for(i=0; i<nColors; i++)
 {
   fGets2(f,Line,100);
   if(Line.length() < CharsPerPixel) continue;
   chLine = Line();
   memcpy(DecodingArea+CharsPerPixel*i, chLine, CharsPerPixel);
   chLine += CharsPerPixel;

   RGBQuad RGB;
   switch(DecodeColorXPM(chLine,RGB))
   {
     case 0: break;
     case 2:  RGB.R>>=8; RGB.G>>=8; RGB.B>>=8;
     default: pPalette->Set(i,&RGB);
   }
 }

 Y = 0;
 if(CharsPerPixel==1)
 {
   while(Y<Height)
   {
     X = 0;
     while(X<Width)
     {
       char ch = fgetc(f);
       if(ch=='\n' || ch=='\r') continue;
       chLine = strchr(DecodingArea,ch);
       Raster->SetValue2D(X,Y, (chLine==NULL)?0:(chLine-DecodingArea));
       X++;
     }
     if(feof(f)) break;
     Y++;
   }
 }
 else if(CharsPerPixel==2)
 {
   while(Y<Height)
   {
     X = 0;
     while(X<Width)
     {
       char ch[2];
       ch[0] = fgetc(f);
       if(ch[0]=='\n' || ch[0]=='\r') continue;
       ch[1] = fgetc(f);
       chLine = DecodingArea;
       j = 0;
       for(i=0; i<nColors; i++)
       {
         if(chLine[0]==ch[0] && chLine[1]==ch[1])
         {
           j = (chLine-DecodingArea) / 2;
           break;
         }
         chLine += 2;
       }
       Raster->SetValue2D(X,Y, j);
       X++;
     }
     if(feof(f)) break;
     Y++;
   }
 }

ENDPROC:
 fclose(f);
 if(DecodingArea) {free(DecodingArea);DecodingArea=NULL;}
 Img.AttachRaster(Raster);
 Img.AttachPalette(pPalette);
 return(Img);
}


class CformatReader
{
public:
    CformatReader(const char*Name) {ReaderStatus=0;F=(Name==NULL)?NULL:fopen(Name,"rb");BufPos=0xFF;}
    ~CformatReader() {if(F!=NULL) {fclose(F);F=NULL;}}

    FILE *F;
    char Buffer[32];
    char ReaderStatus;
    uint8_t BufPos;

    void GetCLine(string &str, int MaxLimit);
    char GetWrappedChar(void);
};


void CformatReader::GetCLine(string &str, int MaxLimit)
{
char ch;
 str.erase();
 if(feof(F)) return;
 do
 {
  do
    {
      if(BufPos>=sizeof(Buffer))
      {
        BufPos = fread(Buffer,1,sizeof(Buffer),F);
        if(BufPos<sizeof(Buffer))
            Buffer[BufPos]=0;		// On feof BuffPos will be 0.
        BufPos = 0;			// Add a terminator after incomplete read.
      }
      ch = Buffer[BufPos++];
      if(ch=='\n' || ch=='\r')
      {
        if(!str.isEmpty()) return;
        continue;
      }
    } while(0);

    switch(ReaderStatus)
    {
      case 0: if(ch=='"') {ReaderStatus=1;break;}
              if(ch=='/') {ReaderStatus=2;break;}
              break;
      case 1: if(ch!='"')
              {
                str += ch;	// Char is inside C string.
                break;
              }
              ReaderStatus=0;
              break;
      case 2: if(ch=='*') {ReaderStatus=3;break;}
              ReaderStatus=0;
              break;
      case 3: if(ch=='*') ReaderStatus=4;
              break;
      case 4: if(ch=='/') ReaderStatus=0;	//End of comment.
              break;
    }
  } while(ch!=0);
}


char CformatReader::GetWrappedChar(void)
{
char ch;

  do
  {
    do
    {
      if(BufPos>=sizeof(Buffer))
      {
        BufPos = fread(Buffer,1,sizeof(Buffer),F);
        if(BufPos<sizeof(Buffer))
            Buffer[BufPos]=0;	// On feof BuffPos will be 0.
        BufPos = 0;					// Add a terminator after incomplete read.
      }
      ch = Buffer[BufPos++];
    } while(ch=='\n' || ch=='\r');

    switch(ReaderStatus)
    {
      case 0: if(ch=='"') {ReaderStatus=1;break;}
              if(ch=='/') {ReaderStatus=2;break;}
              break;
      case 1: if(ch!='"') return ch;	// Char is inside C string.
              ReaderStatus=0;
              break;
      case 2: if(ch=='*') {ReaderStatus=3;break;}
              ReaderStatus=0;
              break;
      case 3: if(ch=='*') ReaderStatus=4;
              break;
      case 4: if(ch=='/') ReaderStatus=0;	//End of comment.
              break;
    }
  } while(ch!=0);
  return 0;
}


Image LoadPictureXPM3(const char *Name)
{
Raster2DAbstract *Raster = NULL;
APalette *pPalette = NULL;
Image Img;
string Line;
char *chLine;
char *DecodingArea = NULL;
unsigned i;
int j;
uint16_t Width, Height;
uint16_t X,Y;
uint32_t nColors;
uint16_t CharsPerPixel;
uint8_t Planes;
uint16_t *AcceleratorMinMax = NULL;
CformatReader CReader(Name);

 if(CReader.F==NULL) return(Img);
 fGets2(CReader.F,Line,80);
 if(Line=="/* XPM */")
     fGets2(CReader.F,Line,80);
 if(Line.isEmpty()) goto ENDPROC;

 if(strncmp(Line(),"static char *",13))  goto ENDPROC;
 chLine = Line() + 13;
 while(isspace(*chLine)) chLine++;
 while(isalnum(*chLine) || *chLine=='_')
   chLine++;
 if(strncmp(chLine,"[] = {",6)) goto ENDPROC;

 CReader.GetCLine(Line,80);
 if(Line.isEmpty()) goto ENDPROC;
 for(i=0; i<Line.length(); i++)
 {
   if(!isspace(Line[i]) && !isdigit(Line[i]))  goto ENDPROC;	// Image is invalid.
 }
 chLine = Line();
 Width = atoi(chLine);
 if(Width==0) goto ENDPROC;
 while(isdigit(*chLine)) chLine++;
 while(isspace(*chLine)) chLine++;
 if(*chLine==0) goto ENDPROC;
 Height = atoi(chLine);
 if(Height==0) goto ENDPROC;
 while(isdigit(*chLine)) chLine++;
 while(isspace(*chLine)) chLine++;
 if(*chLine==0) goto ENDPROC;
 nColors = atoi(chLine);
 while(isdigit(*chLine)) chLine++;
 while(isspace(*chLine)) chLine++;
 if(*chLine==0) goto ENDPROC;
 CharsPerPixel = atoi(chLine);
 if(CharsPerPixel > 4) goto ENDPROC;	// Not supported.
 chLine = NULL;

 if(nColors<=2) Planes=1;
 else if(nColors<=4) Planes=2;
 else if(nColors<=16) Planes=4;
 else if(nColors<=256) Planes=8;
 else if(nColors<=65536) Planes=16;
 else goto ENDPROC;	// Not supported >65536 colors.

 pPalette = BuildPalette(nColors, 16);
 if(pPalette==NULL) goto ENDPROC;

 DecodingArea = (char*)calloc(nColors+1,CharsPerPixel);
 if(DecodingArea == NULL) goto ENDPROC;

 if(CharsPerPixel > 1)
 {
   AcceleratorMinMax = (uint16_t*)malloc(4*nColors*sizeof(uint16_t));
   if(AcceleratorMinMax)
     for(i=0; i<nColors; i++)
     {
       AcceleratorMinMax[2*i] = nColors-1;		// Feed maximal value to min
       AcceleratorMinMax[2*i+1] = 0;			// Feed 0 to max.
     }
 }

 {
   Raster1D_1Bit PaletteClasif;
   bool Palette16Bit = false;
   RGBQuad RGB;

   PaletteClasif.Allocate1D(nColors);
   for(i=0; i<nColors; i++)
   {
     CReader.GetCLine(Line,80);
     if(Line.length() < CharsPerPixel) continue;
     chLine = Line();

     if(AcceleratorMinMax)
     {
       uint16_t *CurAccel = AcceleratorMinMax + *chLine*4;
       if(*CurAccel > i) *CurAccel=i;
       CurAccel++;
       if(*CurAccel < i) *CurAccel=i;

       CurAccel = AcceleratorMinMax + chLine[1]*4+2;
       if(*CurAccel > i) *CurAccel=i;
       CurAccel++;
       if(*CurAccel < i) *CurAccel=i;
     }
     memcpy(DecodingArea+CharsPerPixel*i, chLine, CharsPerPixel);
     chLine += CharsPerPixel;

     switch(DecodeColorXPM(chLine,RGB))
     {
       case 0: break;
       case 2:  PaletteClasif.SetValue1D(i,1);		// 16 byte.
                Palette16Bit = true;
                pPalette->Set(i,&RGB);
                break;
       default: PaletteClasif.SetValue1D(i,0);
                pPalette->Set(i,&RGB);
     }
   }

   if(Palette16Bit)
   {
     for(i=0; i<nColors; i++)
     {
       if(PaletteClasif.GetValue1D(i)==0)
       {
         pPalette->Get(i,&RGB);
         RGB.R <<= 8;
         RGB.G <<= 8;
         RGB.B <<= 8;
         pPalette->Set(i,&RGB);
       }
     }
   }
   else
   {
     APalette *ReducedPal = BuildPalette(nColors, 8);
     for(i=0; i<nColors; i++)
     {
       pPalette->Get(i,&RGB);
       ReducedPal->Set(i,&RGB);
     }
     delete pPalette;
     pPalette = ReducedPal;
   }

   if(GrayPalette(pPalette))
   {
     delete(pPalette);
     pPalette = NULL;
   }
 }

 Raster = CreateRaster2D(Width,Height,Planes);
 if(Raster==NULL) goto ENDPROC;

 Y = 0;
 if(CharsPerPixel==1)
 {
   while(Y<Height)
   {
     X = 0;
     while(X<Width)
     {
       char ch = CReader.GetWrappedChar();
       if(ch==0) break;
       chLine = strchr(DecodingArea,ch);
       Raster->SetValue2D(X,Y, (chLine==NULL)?0:(chLine-DecodingArea));
       X++;
     }
     if(feof(CReader.F)) break;
     Y++;
   }
 }
 else
 {
   Raster1DAbstract *pRow;
   char ch[4];

   while(Y<Height)
   {
     pRow = Raster->GetRowRaster(Y);
     if(pRow==NULL) break;

     X = 0;
     while(X<Width)
     {
       for(j=0; j<CharsPerPixel; j++)
         ch[j] = CReader.GetWrappedChar();
		// No need to test EOF here, it is sufficient enough to test on the last char only.
       if(ch[CharsPerPixel-1]==0) break;		// EOF reached.

       i = AcceleratorMinMax[ch[0]*4];				// Minimum according to CHAR0
       j = AcceleratorMinMax[ch[1]*4+2];			// Minimum according to CHAR1
       chLine = DecodingArea + CharsPerPixel*((i>j) ? i : j);		// Max from minims.
       i = AcceleratorMinMax[ch[0]*4+1];
       j = AcceleratorMinMax[ch[1]*4+2+1];
       const char *EndLine = DecodingArea + CharsPerPixel*((i<j) ? i : j);	// Min from maxims.

       j = 0;
       while(chLine <= EndLine)
       {
         if(memcmp(chLine,ch,CharsPerPixel)==0)
         {
           j = (chLine-DecodingArea) / CharsPerPixel;
           break;		//goto BREAK_CYCLE;
         }
         chLine += CharsPerPixel;
       }

       pRow->SetValue1D(X, j);			// Unreferenced value should not occur.
       X++;
     }
     if(feof(CReader.F)) break;
     Y++;
   }
 }

ENDPROC:
 if(DecodingArea) {free(DecodingArea);DecodingArea=NULL;}
 Img.AttachRaster(Raster);
 Img.AttachPalette(pPalette);
 if(AcceleratorMinMax) free(AcceleratorMinMax);
 return(Img);
}


#endif


#if SupportXPM>=3


int SavePictureXPM3(const char *Name,const Image &Img)
{
FILE *f;
uint16_t *Histogram = NULL;
int32_t MaxColors;
uint16_t y,x;
uint8_t CharsPerPixel;
uint8_t *ColorMap;
int i, j;

  if(Img.Raster==NULL) return(ErrEmptyRaster);
  if(Img.Raster->GetPlanes()>16) return(-11);

  MaxColors = 1<<Img.Raster->GetPlanes();

 if(MaxColors <= 65536)
 {
   Histogram = (uint16_t*)calloc(MaxColors,sizeof(uint16_t));
   for(y=0; y<Img.Raster->Size2D; y++)
   {
     Raster1DAbstract *LineRas = Img.Raster->GetRowRaster(y);
     for(x=0; x<Img.Raster->Size1D; x++)
     {
       Histogram[LineRas->GetValue1D(x)] = 1;
     }
   }
   j = 0;
   for(i=0; i<MaxColors; i++)
   {
     if(Histogram[i]!=0)
         Histogram[i] = j++;
     else
         Histogram[i] = 0xFFFF;
   }
   MaxColors = j;
   if(MaxColors>=0xFFFF)	// Histogram is void of use for full rank.
   {
     free(Histogram);
     Histogram = NULL;
   }
 }

 CharsPerPixel = 1;
 j = MaxColors;
 while(j > ('~'-' '-2))
 {
   CharsPerPixel++;
   j /= ('~'-' '-2);
 }

  ColorMap = (uint8_t*)malloc(MaxColors*CharsPerPixel+1);
  if(ColorMap==NULL) return -12;

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

  fprintf(f,"/* XPM */\n"
            "static char *sample[] = {\n"
            "/* columns rows colors chars-per-pixel */\n"
            "\"%u %u %u %u\",\n",
		Img.Raster->Size1D, Img.Raster->Size2D, MaxColors, (unsigned)CharsPerPixel);

  APalette *pPalette = Img.Palette;
  if(pPalette==NULL)
  {
    pPalette = BuildPalette(1<<Img.Raster->GetPlanes(), (Img.Raster->GetPlanes()<=8)?8:16);
    if(pPalette==NULL) goto ENDPROC;
    FillGray(pPalette);
  }

  j = -1;
  for(i=0; i<1<<Img.Raster->GetPlanes(); i++)
  {
    if(Histogram)
    {
      if(Histogram[i]==0xFFFF) continue;
      if(j<MaxColors-1) j++;
    }
    else
      j = i;

    uint8_t *ColorItem = ColorMap + j*CharsPerPixel;
    int ColorStr = j;
    for(x=0; x<CharsPerPixel; x++)
    {
      ColorItem[x] = ' ' + (ColorStr % ('~'-' '-2));
      if(ColorItem[x] >= '"') ColorItem[x]++;
      if(ColorItem[x] >= '\\') ColorItem[x]++;
      ColorStr /= '~'-' '-2;
    }
    ColorItem[CharsPerPixel] = 0;	// There is granted at least one byte behing the last symbol.
    RGBQuad RGB;
    pPalette->Get(i, &RGB);
    fprintf(f, (pPalette->GetPlanes()<=8*pPalette->Channels()) ? "\"%s\tc #%2.2X%2.2X%2.2X\",\n" : "\"%s c #%4.4X%4.4X%4.4X\",\n" ,
              ColorItem, RGB.R, RGB.G, RGB.B);
  }
  fputs("/* pixels */\n", f);

  for(y=0; y<Img.Raster->Size2D; y++)
  {
    Raster1DAbstract *pRow = Img.Raster->GetRowRaster(y);
    if(pRow==NULL) continue;

    fputs("\"",f);
    for(x=0; x<Img.Raster->Size1D; x++)
    {
      uint16_t val = pRow->GetValue1D(x);
      if(Histogram) val = Histogram[val];
      fwrite(ColorMap + val*CharsPerPixel, CharsPerPixel, 1, f);
    }
    fputs((y<Img.Raster->Size2D-1)?"\",\n":"\"\n",f);
  }
  fputs("};",f);

ENDPROC:
  fclose(f);
  free(ColorMap);
  if(pPalette!=Img.Palette && pPalette!=NULL)
  {
    delete pPalette;
    pPalette = NULL;
  }
  if(Histogram!=NULL)
  {
    free(Histogram);
  }
  return(0);
}


#endif

#endif
//-------------------End of XPM routines------------------

