//================================================================
// RGBE (RGB + common exponent HDR format) codec
//
// AUTHOR:  Gabor Nagy
// DATE:    2018-Apr-01 02:39:19
//
// Copyright (C) 2014-2026 by Gabor Nagy. All rights reserved.
//================================================================

// Is decomposing a float via a union portable?
typedef union
{
  float  f32;
  struct
  {
    uint32_t  Mantissa : 23;
    uint32_t  Exponent : 8;
    uint32_t  Sign : 1;
  };
} EFloat32Cast;


//static inline void Ep_RGBf32ToRGBE8(const float PR, const float PG, const float PB,  uint8_t** PRowBuffers, const EIndex PX)
static inline void Ep_RGBf32ToRGBE8(const float PR, const float PG, const float PB,  void* POut, const EIndex PX)
{
	EFloat32Cast  LRF, LGF, LBF;

	LRF.f32 = PR;
	LGF.f32 = PG;
	LBF.f32 = PB;

	uint32_t  LMantissaR = LRF.Mantissa;
	int16_t   LExponentR = (int16_t)LRF.Exponent;if(LExponentR) LMantissaR |= 0x800000;

	uint32_t  LMantissaG = LGF.Mantissa;
	int16_t   LExponentG = (int16_t)LGF.Exponent;if(LExponentG) LMantissaG |= 0x800000;

	uint32_t  LMantissaB = LBF.Mantissa;
	int16_t   LExponentB = (int16_t)LBF.Exponent;if(LExponentB) LMantissaB |= 0x800000;

//printf("%s() %s:%d  %06x % 3d ->", __FUNCTION__, __FILE__, __LINE__, LMantissaR, LExponentR);fflush(stdout);

// Use the largest value, for determining the common exponent
//
	int  LExpFrom;
	if(LExponentR > LExponentG)
	{
		if(LExponentR > PB) LExpFrom = 0;
		else LExpFrom = 2;
	}
	else
	{
		if(LExponentG > LExponentB) LExpFrom = 1;
		else LExpFrom = 2;
	}

	int  LR, LG, LB, LE;
	switch(LExpFrom)
	{
		case 0:
			{
				const int  LExpDiff0 = LExponentR - LExponentG, LExpDiff1 = LExponentR - LExponentB;
				LR = LMantissaR >> 16;
				LG = LMantissaG >> (16 + LExpDiff0);
				LB = LMantissaB >> (16 + LExpDiff1);
				LE = LExponentR;
			}
		break;

		case 1:
			{
				const int  LExpDiff0 = LExponentG - LExponentR, LExpDiff1 = LExponentG - LExponentB;
				LR = LMantissaR >> (16 + LExpDiff0);
				LG = LMantissaG >> 16;
				LB = LMantissaB >> (16 + LExpDiff1);
				LE = LExponentG;
			}
		break;

		case 2:
			{
				const int  LExpDiff0 = LExponentB - LExponentR, LExpDiff1 = LExponentB - LExponentG;
				LR = LMantissaR >> (16 + LExpDiff0);
				LG = LMantissaG >> (16 + LExpDiff1);
				LB = LMantissaB >> 16;
				LE = LExponentB;
			}
		break;
	}

	LE += 0x2;

	_RGBEOut(LR, LG, LB, LE,  POut, PX);
}


static inline void Ep_RGBE8ToRGBf32(const uint8_t* PIn, float* PR, float* PG, float* PB)
{
	const uint8_t   LRGBEBase = 128;
	const uint8_t*  LIn = PIn;
	const uint8_t   LR = *LIn++;
	const uint8_t   LG = *LIn++;
	const uint8_t   LB = *LIn++;
	const uint8_t   LE = *LIn++;

	if(LE == 0 || LE > 0x98)
	{
		*PR = *PG = *PB = 0.0;
	}
	else
	{
		const double  LV = ldexp(1.0, (int)LE - (LRGBEBase + 8));

		*PR = LV * ((float)LR + 0.5);
		*PG = LV * ((float)LG + 0.5);
		*PB = LV * ((float)LB + 0.5);

/*
printf("%s() %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
assert(LDstPixel->R >= 0.0);
assert(LDstPixel->G >= 0.0);
assert(LDstPixel->B >= 0.0);
*/
	}
}
