/******************************************************************************
 * program:     rasimg library                                                *
 * function:    Miscellaneous operations with raster.                         *
 * modul:       imageop.cc                                                    *
 * licency:     GPL or LGPL                                                   *
 ******************************************************************************/
#include <stddef.h>
#include <stdio.h>

#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "typedfs.h"
#include "image.h"
#include "ras_prot.h"

#include "matrix.h"


//#include <alloc.h>

#ifndef MIN
 #define MIN(a,b) ((a)<(b) ? (a) : (b))
#endif


////////////////////////////////////////////////////////////////////

Image PeelPlane(const Image Img, int PlaneNum)
{
Raster2DAbstract *Raster=NULL;
Image NewImg;
int y;

 if(Img.Raster!=NULL)
  {
  Raster = CreateRaster2D(Img.Raster->Size1D,Img.Raster->Size2D,1);
  if(Raster)
    for(y=0; y<Img.Raster->Size2D; y++)		// main row loop
      {
      Img.Raster->GetRowRaster(y)->Peel1Bit(Raster->GetRow(y),PlaneNum);
      }
  }

  if(Raster!=NULL) NewImg.AttachRaster(Raster);
  if(Img.Palette!=NULL) NewImg.AttachPalette(Img.Palette);

return(NewImg);
}



Image Peel8bit(const Image Img, int PlaneNum8)
{
Raster2DAbstract *Raster=NULL;
Image NewImg;
int y;

 if(Img.Raster!=NULL)
  {
  PlaneNum8 *= 8;
  Raster = CreateRaster2D(Img.Raster->Size1D,Img.Raster->Size2D,8);

  if(Raster)
    for(y=0; y<Img.Raster->Size2D; y++)		// main row loop
      {
      Img.Raster->GetRowRaster(y)->Peel8Bit((unsigned char*)Raster->GetRow(y),PlaneNum8);
      }
  }

  if(Raster!=NULL) NewImg.AttachRaster(Raster);
  if(Img.Palette!=NULL) NewImg.AttachPalette(Img.Palette);

return(NewImg);
}


////////////////////////////////////////////////////////////////////


void AddLB(unsigned Size,uint32_t *Accu, uint8_t *Add)
{
 while(Size--)
 {
  *Accu++ += *Add++;
 }
}

void SubLB(unsigned Size,uint32_t *Accu, uint8_t *Sub)
{
 while(Size--)
 {
  *Accu++ -= *Sub++;
 }
}


Image Mask(const Image &im_in, int size)
{
Image im_out;
//Raster2DAbstract *Raster=NULL;
uint16_t k2;
uint32_t k;
uint16_t i,j;
uint16_t x,y;
int y1,y2,x1,x2;
uint32_t aLl;
uint32_t *ptrLine;
uint8_t *Sptr;
uint32_t *SptrAl,*SptrAr;
Raster1D_8Bit ptrLineB;
uint8_t color,clr;

 if(size==0 || im_in.Raster==NULL) return im_out;

 color = 1;
 if(im_in.ImageType()==ImageTrueColor)
   {
   color = 3;
   //  Obr2.typ:='C';
   //if AlineProc<>nil then AlineProc^.pases := AlineProc^.pases*3;
   }

 im_out.Create(im_in.Raster->GetSize1D(),im_in.Raster->Size2D,im_in.Raster->GetPlanes());
 if(im_out.Raster==NULL || im_out.Raster->Data1D==NULL) return im_out;

 //if( AlineProc!=NULL) AlineProc->InitPassing(Obr1.y,'Filtering by MASK');

 ptrLine = (uint32_t *)malloc(4*im_in.Raster->GetSize1D());
 ptrLineB.Allocate1D(im_in.Raster->GetSize1D());

 k = size*size;
 k2 = k/2;
 x = size/2 + 1;
 y = size/2 + 1;

 for(clr=0;clr<color;clr++)
   {
   y1 = x + 1;
   if(size<=2) y1--;		//pocet pridanych radek

   memset(ptrLine,0,4*im_in.Raster->GetSize1D());
   if(color==1)
      ptrLineB.Set(*im_in.Raster->GetRowRaster(0));
   else
     im_in.Raster->GetRowRaster(0)->Peel8Bit((uint8_t *)ptrLineB.Data1D,8*clr);


   for(j=0; j<y1; j++) 
     AddLB(ptrLineB.GetSize1D(),ptrLine,(uint8_t *)ptrLineB.Data1D); /* ukradek=y1*ukradekW*/

   for(y1=1;y1<=(int)(size+1)/2 - 2;y1++)
     {		// pridani pripravnych radku}
     if(color==1)
       ptrLineB.Set(*im_in.Raster->GetRowRaster(y1));
     else
       im_in.Raster->GetRowRaster(y1)->Peel8Bit((uint8_t *)ptrLineB.Data1D,8*clr);
     AddLB(ptrLineB.GetSize1D(),ptrLine,(uint8_t *)ptrLineB.Data1D);
     }

   for(i=0;i<im_out.Raster->Size2D;i++)	// main filtration loop
    {
    y1 = (int)(i)-y;
    y2 = y1+size;
    if(y1<0) y1=0;
    if(y2>=im_out.Raster->Size2D) y2 = im_out.Raster->Size2D - 1;

    if(color==1)
      ptrLineB.Set(*im_in.Raster->GetRowRaster(y1));
    else
      im_in.Raster->GetRowRaster(y1)->Peel8Bit((uint8_t *)ptrLineB.Data1D,8*clr);
    SubLB(ptrLineB.GetSize1D(),ptrLine,(uint8_t *)ptrLineB.Data1D); //vyjmuti posledniho radku y1

    if(color==1)
      ptrLineB.Set(*im_in.Raster->GetRowRaster(y2));
    else
      im_in.Raster->GetRowRaster(y2)->Peel8Bit((uint8_t *)ptrLineB.Data1D,8*clr);
    AddLB(ptrLineB.GetSize1D(),ptrLine,(uint8_t *)ptrLineB.Data1D); //pridani noveho radku y2
		//zpracovani obsahu radku
    x1 = y + 1;
    if(size<=2) x1--;		// pocet pridanych sloupcu
    aLl = x1 * ptrLine[0];
    for(x2=1;x2<=(int)(size+1)/2 - 2;x2++) aLl += ptrLine[x2];

    Sptr = (uint8_t *)ptrLineB.Data1D;		//vypocet masky v radku
    x1 = -(int)(x);
    x2 = MIN(x1+size,ptrLineB.GetSize1D()-1);
    SptrAl = ptrLine;
    SptrAr = ptrLine + x2;
    for(j=0;j<ptrLineB.GetSize1D();j++)
      {
      aLl += *SptrAr;
      aLl -= *SptrAl;

      *Sptr = (aLl + k2) / k;

      if(x1>=0)	SptrAl++;
      if(x2<ptrLineB.GetSize1D()-1) SptrAr++;
      Sptr++;
      x1++;
      x2++;
      }

    if(color==1)		// data storage
      ptrLineB.Get(*im_out.Raster->GetRowRaster(i));
    else
      im_out.Raster->GetRowRaster(i)->Join8Bit((uint8_t *)ptrLineB.Data1D,8*clr);

    //if AlineProc<>nil then AlineProc^.NextLine;	{Dialog s uzivatelem}
    }
   }
  ptrLineB.Erase();
  free(ptrLine);  ptrLine=NULL;

return(im_out);
}


///////////////////////////////////////////////////////////////////////////

Image Threshold(Image Img, unsigned ThrValue)
{
Raster2DAbstract *Raster = NULL;
Image NewImg;
Raster1DAbstract *Rsrc, *Rthr;
int x,y;

 if(Img.Raster!=NULL)
  {
  Raster = CreateRaster2D(Img.Raster->Size1D,Img.Raster->Size2D,1);
  if(Raster)
    for(y=0; y<Img.Raster->Size2D; y++)		// main row loop
      {
      Rsrc = Img.Raster->GetRowRaster(y);
      Rthr = Raster->GetRowRaster(y);

      for(x=0; x<Img.Raster->Size1D; x++)
        {
        if(Rsrc->GetValue1D(x)>ThrValue)
          Rthr->SetValue1D(x,1);
        else
          Rthr->SetValue1D(x,0);
        }
      }
  }

  NewImg.AttachRaster(Raster);
return(NewImg);
}


///////////////////////////////////////////////////////


void Convert2Gray(Image &I)
{
Image *I1 = &I;
Raster2DAbstract *Raster = NULL;

  while(I1!=NULL)
  {
    switch(I1->ImageType())
    {
      case ImageNone:
      case ImageGray:
      case ImagePaletteF:
      case ImageReal:
      case ImageSigned:		break;
      
      case ImagePalette:	 //2-palette,
		Raster = CreateRaster2D(I1->Raster->Size1D,I1->Raster->Size2D,8);
                if(Raster)
                {
                  for(unsigned y=0; y<Raster->Size2D; y++)		// main row loop
                  {
                    Raster1DAbstract *RSrc = I1->Raster->GetRowRaster(y);
                    Raster1DAbstract *RDest = Raster->GetRowRaster(y);
                    for(unsigned x=0; x<Raster->Size1D; x++)
                    {
                      uint32_t val = I1->Palette->GetValue1D(RSrc->GetValue1D(x));
                      val = ((val&0xFF) + ((val>>8)&0xFF) + ((val>>16)&0xFF)) / 3;
	              RDest->SetValue1D(x,val);
                    }
                  }
                  I1->AttachRaster(Raster);
                  I1->AttachPalette(NULL);
                  Raster = NULL;
                }
                break;

      case ImageTrueColor:
                Raster = CreateRaster2D(I1->Raster->Size1D,I1->Raster->Size2D,8);
                if(Raster)
                {
                  for(unsigned y=0; y<Raster->Size2D; y++)		// main row loop
                  {
                    Raster1DAbstract *RSrc = I1->Raster->GetRowRaster(y);
                    Raster1DAbstract *RDest = Raster->GetRowRaster(y);
                    for(unsigned x=0; x<Raster->Size1D; x++)
                    {
                      uint32_t val = RSrc->GetValue1D(x);
                      val = ((val&0xFF) + ((val>>8)&0xFF) + ((val>>16)&0xFF)) / 3;
	              RDest->SetValue1D(x,val);
                    }
                  }
                  I1->AttachRaster(Raster); Raster=NULL;  // Ussage count is 1; piinter could be discarded
                  I1->AttachPalette(NULL);                  
                }
                break;
		
    }
    I1 = I1->Next;
  }
}


