/******************************************************************************
 * program:     rasimg library 0.37                                           *
 * function:    Miscellaneous utilities for manipulation with images.         *
 * modul:       rasterut.cc                                                   *
 * licency:     GPL or LGPL                                                   *
 * Copyright: (c) 1998-2025 Jaroslav Fojtik                                   *
 ******************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#include "typedfs.h"
#include "common.h"

#include "raster.h"
#include "ras_prot.h"

#ifdef _MSC_VER  
  #pragma warning(disable: 4244)
  #pragma warning(disable: 4250)	// Inheriting via dominance, standard C++ feature.
#endif


/// Set pixel for 1bpp 1D image data.
void SetValue2(uint8_t *b, unsigned x, uint8_t NewValue)
{
uint8_t v;
 v = NewValue;
 if(NewValue>3) v=3;
 b+= x >> 2;
 switch(x & 3)
   {
   case 0:v=v << 6; *b=*b & 0x3F;	break;
   case 1:v=v << 4; *b=*b & 0xCF;	break;
   case	2:v=v << 2; *b=*b & 0xF3;	break;
   case	3:*b=*b & 0xFC;
   }
 *b = *b | v;
}


/// Get pixel for 1bpp 1D image data.
uint8_t GetValue2(const uint8_t *b, unsigned x)
{
 if(b==NULL) return 0;
 b+= x >> 2;
 switch(x & 3)
   {
   case 0:return( (*b >> 6)&3 );
   case 1:return( (*b >> 4)&3 );
   case	2:return( (*b >> 2)&3 );
   case	3:return( *b & 3 );
   }
return 0;
}


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


#ifdef OPTIMISE_SPEED

void Conv1_2(uint8_t *Dest, const uint8_t *Src, unsigned Size1D)
{
uint8_t Mask1;
uint8_t Mask2;

  while(Size1D>0)
  {
    Mask1 = 0x80;   
    do
      {
      Mask2 = 0xC0;
      if((*Src & Mask1) == 0)
         *Dest = 0;
      else
        *Dest = Mask2;
       
      if(--Size1D<=0) return;
      Mask1 >>= 1;
      Mask2 >>= 2;
      if((*Src & Mask1) != 0) *Dest|=Mask2;

      if(--Size1D<=0) return;
      Mask1 >>= 1;
      Mask2 >>= 2;
      if((*Src & Mask1) != 0) *Dest|=Mask2;

      if(--Size1D<=0) return;
      Mask1 >>= 1;
      Mask2 >>= 2;
      if((*Src & Mask1) != 0) *Dest|=Mask2;

      Dest++;
      if(--Size1D<=0) return;
      Mask1 >>= 1;
      } while(Mask1!=0);
    Src++;
  }
}


void Conv2_1(uint8_t *Dest, const uint8_t *Src, unsigned Size1D)
{
uint8_t Mask;

  while(Size1D>0)
  {
    *Dest = 0;
    for(Mask=0x80; Mask!=0; Mask>>=1)
    {
      if(*Src>=0x80) *Dest|=Mask;
      if(--Size1D<=0) return;
      Mask >>= 1;
      if((0x3F & *Src)>=0x20) *Dest|=Mask;
      if(--Size1D<=0) return;
      Mask >>= 1;
      if((0xF & *Src)>=0x08) *Dest |= Mask;
      if(--Size1D<=0) return;
      Mask >>= 1;
      if((0x3 & *Src++)>=0x02) *Dest |= Mask;
      if(--Size1D<=0) return;
    }
    Dest++;
  }
}


void Conv2_8(uint8_t *Dest, const uint8_t *Src, unsigned Size1D)
{
uint8_t Shift;

  while(Size1D>0)
  {
    Shift = 8;
    do
    {
      Shift -= 2;
      *Dest++ = ((*Src >> Shift) & 3) * 0x55;
      if(--Size1D<=0) return;
    } while(Shift>0);
    Src++;
  }
}


void Conv2_16(uint16_t *Dest, const uint8_t *Src, unsigned Size1D)
{
uint8_t Shift;

  while(Size1D>0)
  {
    Shift = 8;
    do
    {
      Shift -= 2;
      *Dest++ = ((*Src >> Shift) & 3) * 0x5555;
      if(--Size1D<=0) return;
    } while(Shift>0);
    Src++;
  }
}


void Conv2_24(uint8_t *Dest, const uint8_t *Src, unsigned Size1D)
{
uint8_t Shift;

  while(Size1D>0)
  {
    Shift = 8;
    do
    {
      Shift -= 2;
      Dest[0] = Dest[1] = Dest[2] = ((*Src >> Shift) & 3) * 0x55;
      Dest+=3;
      if(--Size1D<=0) return;
    } while(Shift>0);
    Src++;
  }
}


void Conv2_32(uint32_t *Dest, const uint8_t *Src, unsigned Size1D)
{
uint8_t Shift;

  while(Size1D>0)
  {
    Shift = 8;
    do
    {
      Shift -= 2;
      *Dest++ = ((*Src >> Shift) & 3) * 0x55555555;
      if(--Size1D<=0) return;
    } while(Shift>0);
    Src++;
  }
}

#endif


#if defined(uint64_t_defined)

/*
void Conv64_8(uint8_t *Dest, const uint64_t *Src, unsigned Size1D)
{
  while(Size1D-->0)
  {
    *Dest++ = *Src++ >> 56;
  }
}
*/


void Conv64_16(uint16_t *Dest, const uint64_t *Src, unsigned Size1D)
{
  while(Size1D-->0)
  {
    *Dest++ = *Src++ >> 48;
  }
}


void Conv64_24(uint8_t *Dest, const uint64_t *Src, unsigned Size1D)
{
  while(Size1D-->0)
  {		// NOTE: x = *Src does not speedup code.
    *Dest++ = 0xFF & (*Src >> 40);
    *Dest++ = 0xFF & (*Src >> 48);
    *Dest++ = 0xFF & (*Src >> 56);
    Src++;
  }
}

#endif


//------------------------------------------------------------


/** This procedure provides flipping of the 1D vector. */
void Flip1D(Raster1DAbstract *r1D)
{
  if(r1D==NULL) return;
  if(r1D->Data1D==NULL) return;
  const int p = labs(r1D->GetPlanes());

  switch(p)
  {
    case 1:  Flip1((uint8_t*)r1D->GetRow(), r1D->GetSize1D());
	     return;
    case 2:  Flip2((uint8_t*)r1D->GetRow(), r1D->GetSize1D());
	     return;
    case 4:  Flip4((uint8_t*)r1D->GetRow(), r1D->GetSize1D());
	     return;
    case 8:  Flip8((uint8_t*)r1D->GetRow(), r1D->GetSize1D());
	     return;
    case 16: Flip16((uint16_t*)r1D->GetRow(), r1D->GetSize1D());
	     return;
    case 24: Flip24((uint8_t*)r1D->GetRow(), r1D->GetSize1D());
	     return;
    case 32: Flip32((uint32_t*)r1D->GetRow(), r1D->GetSize1D());
	     return;
    case 64: Flip64((uint64_t_fix*)r1D->GetRow(), r1D->GetSize1D());
	     return;
  }
  FlipN((uint8_t*)r1D->GetRow(), r1D->GetSize1D(),p);
}


/** This procedure provides vertical flipping of the raster. */
void Flip2D(Raster2DAbstract *r2D)
{
 if(r2D==NULL) return;
 if(r2D->Data2D==NULL) return;

#if defined(POINTER_SIZE) && POINTER_SIZE==32
  Flip32((uint32_t*)r2D->Data2D, r2D->GetSize2D());
#else
 #if defined(POINTER_SIZE) && POINTER_SIZE==64 && (defined(uint64_t_defined))
  Flip64((uint64_t*)r2D->Data2D, r2D->GetSize2D());
 #else
  void *ptr, **P1, **P2;
  P1 = r2D->Data2D;
  P2 = P1 + (r2D->Size2D-1); 
  while(P2>P1)
    {
    ptr = *P1;
    *P1 = *P2;
    *P2 = ptr;
    P1++; P2--;
    }
 #endif
#endif
}


/** This procedure provides flipping along 1st dimension (usually horizontal)
 * of the raster. */
void Flip1D(Raster2DAbstract *r2D)
{
unsigned y;

  if(r2D==NULL) return;
  if(r2D->Data2D==NULL) return;

  const int p = labs(r2D->GetPlanes());
  const unsigned Size1D = r2D->GetSize1D();

/*
  if(p<8 && p!=4)	// Speedup for <4 bits
    {
    Raster1D_8Bit Buff;
    Buff.Allocate1D(r2D->GetSize1D());
    if(Buff.Data1D==NULL) return;	// Report error here.

    for(y=0; y<r2D->Size2D; y++)
      {
      r2D->GetRowRaster(y)->Get(Buff);
      Flip8((uint8_t*)Buff.Data1D, r2D->GetSize1D());
      r2D->GetRowRaster(y)->Set(Buff);
      }
    Buff.Erase();
    return;
    }
*/

  for(y=0; y<r2D->Size2D; y++)
    {
    switch(p)
      {
      case 1:  Flip1((uint8_t*)r2D->GetRow(y), Size1D);
	       break;
      case 2:  Flip2((uint8_t*)r2D->GetRow(y), Size1D);
	       break;
      case 4:  Flip4((uint8_t*)r2D->GetRow(y), Size1D);
	       break;
      case 8:  Flip8((uint8_t*)r2D->GetRow(y), Size1D);
	       break;
      case 16: Flip16((uint16_t*)r2D->GetRow(y), Size1D);
	       break;
      case 24: Flip24((uint8_t*)r2D->GetRow(y), Size1D);
	       break;
      case 32: Flip32((uint32_t*)r2D->GetRow(y), Size1D);
	       break;
      case 64: Flip64((uint64_t_fix*)r2D->GetRow(y), Size1D);
	       break;
/*
      default: {
                 unsigned x1 = 0;
	         unsigned x2 = r2D->Size1D - 1;
	         while(x1<x2)
		 {
		   double tmp = r2D->GetValue2Dd(x1,y);
		   r2D->SetValue2Dd(x1,y, r2D->GetValue2Dd(x2,y));
		   r2D->SetValue2Dd(x2,y, tmp);
		   x1++;
		   x2--;
		 }
               }
*/
      }
   }
}


#if defined(RASTER_3D)

/** This procedure provides flipping along 1st dimension of the raster. */
void Flip1D(Raster3DAbstract *r3D)
{
unsigned y, z;

  if(r3D==NULL) return;
  if(r3D->Size1D<=1 || r3D->Data3D==NULL) return;

  const int p = labs(r3D->GetPlanes());
  const unsigned Size1D = r3D->GetSize1D();

/*
  if(p<8 && p!=4)	// Speedup for less than 8 bits
    {
    Raster1D_8Bit Buff;
    Buff.Allocate1D(r3D->GetSize1D());
    if(Buff.Data1D==NULL) return;

    for(z=0; z<r3D->Size3D; z++)
      for(y=0; y<r3D->Size2D; y++)
	{
	r3D->GetRowRaster(y,z)->Get(Buff);
	Flip8((uint8_t*)Buff.Data1D, r3D->GetSize1D());
	r3D->GetRowRaster(y,z)->Set(Buff);
	}
    Buff.Erase();
    return;
    }
*/

  for(z=0; z<r3D->Size3D; z++)
  {
    void **Row2D = r3D->GetRow(z);
    for(y=0; y<r3D->Size2D; y++)
    {
    switch(p)
      {
      case 1:  Flip1((uint8_t*)Row2D[y], Size1D);
	       break;
      case 2:  Flip2((uint8_t*)Row2D[y], Size1D);
	       break;
      case 4:  Flip4((uint8_t*)Row2D[y], Size1D);
	       break;
      case 8:  Flip8((uint8_t*)Row2D[y], Size1D);
	       break;
      case 16: Flip16((uint16_t*)Row2D[y], Size1D);
	       break;
      case 24: Flip24((uint8_t*)Row2D[y], Size1D);
	       break;
      case 32: Flip32((uint32_t*)Row2D[y], Size1D);
	       break;
      case 64: Flip64((uint64_t_fix*)Row2D[y], Size1D);
	       break;
/*
      default: unsigned x1 = 0;
	       unsigned x2 = r3D->Size1D - 1;
               
	       while(x1<x2)
		 {
		 double tmp = r3D->GetValue3Dd(x1,y,z);
		 r3D->SetValue3Dd(x1,y,z, r3D->GetValue3Dd(x2,y,z));
		 r3D->SetValue3Dd(x2,y,z, tmp);
		 x1++;
		 x2--;
		 }
*/               
       }
    }
 }
}


/** This procedure provides flipping along 2nd dimension of the raster. */
void Flip2D(Raster3DAbstract *r3D)
{
 if(r3D==NULL) return;
 if(r3D->Size2D==0 || r3D->Data3D==NULL) return;

 for(unsigned z=0; z<r3D->Size3D; z++)
 {
#if defined(POINTER_SIZE) && POINTER_SIZE==32
   Flip32((uint32_t*)r3D->Data3D[z], r3D->GetSize2D());
#else
 #if defined(POINTER_SIZE) && POINTER_SIZE==64 && (defined(uint64_t_defined))
   Flip64((uint64_t*)r3D->Data3D[z], r3D->GetSize2D());
 #else
   void *ptr, **P1, **P2;
   P1 = r3D->Data3D[z];
   P2 = r3D->Data3D[z] + (r3D->Size2D-1);
   while(P2>P1)
     {
     ptr = *P1;
     *P1 = *P2;
     *P2 = ptr;
     P1++; P2--;
     }
 #endif
#endif
 }
}


/** This procedure provides flipping along 3rd dimension of the raster. */
void Flip3D(Raster3DAbstract *r3D)
{
  if(r3D==NULL) return;
  if(r3D->Size3D==0 || r3D->Data3D==NULL) return;

#if defined(POINTER_SIZE) && POINTER_SIZE==32
  Flip32((uint32_t*)r3D->Data3D, r3D->GetSize3D());
#else
 #if defined(POINTER_SIZE) && POINTER_SIZE==64 && (defined(uint64_t_defined))
  Flip64((uint64_t*)r3D->Data3D, r3D->GetSize3D());
 #else
  void **ptr, ***P1, ***P2;
  P1 = r3D->Data3D;
  P2 = r3D->Data3D + (r3D->Size3D-1);
  while(P2>P1)
    {
    ptr = *P1;
    *P1 = *P2;
    *P2 = ptr;
    P1++; P2--;
    }
 #endif
#endif
}

#endif


Raster2DAbstract *Flip1D2D(Raster2DAbstract *r2D)
{
Raster2DAbstract *rFlip;
unsigned x;
  if(r2D->Data2D==NULL || r2D->Size2D==0 || r2D->Size1D==0) return NULL;

  const int p = r2D->GetPlanes();
  switch(r2D->Channels())
  {
    case 1: rFlip = CreateRaster2D(r2D->Size2D, r2D->Size1D, p);
	    break;
    case 3: rFlip = CreateRaster2DRGB(r2D->Size2D, r2D->Size1D, p/3);
	    break;
    case 4: rFlip = CreateRaster2DRGB(r2D->Size2D, r2D->Size1D, p/4);
	    break;
    default:return NULL;
  }
  if(rFlip==NULL) return NULL;
  if(rFlip->Data2D==NULL)
  {
    RaiseError(RasterId|No_Memory,rFlip);
    delete rFlip;
    return NULL;
  }

  for(unsigned y=0; y<r2D->Size2D; y++)
  {
    x = r2D->Size1D;
    const Raster1DAbstract *r1D = r2D->GetRowRaster(y);
    if(r2D->Channels()==1)
    {
      if(p <= -32)		// -32 is for floar and -64 for double.
      {
        while(x-- > 0)
        {
          rFlip->SetValue2Dd(y,x, r1D->GetValue1Dd(x));
        }
      }
      else
      {
        while(x-- > 0)
        {
          rFlip->SetValue2D(y,x, r1D->GetValue1D(x));
        }
      }
    }
    else
    {
      RGBQuad RGB;      
      while(x-- > 0)
      {                  
        r1D->Get(x,&RGB);
        rFlip->Set(y,x,&RGB);
      }
    }
  }

return rFlip;
}


void FlipN(uint8_t *d, unsigned len, unsigned char BPP)
{
uint8_t *d2, B;
unsigned char BPP2;
  if(len<=1) return;
  if((BPP & 7)!=0) return;
  BPP >>= 3;

  d2 = d + (len-1)*BPP;
  while(d2 > d)
  {
    for(BPP2=0; BPP2<BPP; BPP2++)
    {
      B=d[BPP2]; d[BPP2]=d2[BPP2]; d2[BPP2]=B;
    }
    d += BPP;
    d2 -= BPP;
  }
}


