#include "texture.h"

void set_pixel_rgb24(int x, int y, Uint32 pixel, SDL_Surface* surface) {
	Uint8* pixels_pointer = (Uint8*)surface->pixels;
	int pitch = surface->pitch;
	Uint8* p = pixels_pointer + y*pitch + x*surface->format->BytesPerPixel;
	#if SDL_BYTEORDER==SDL_BIG_ENDIAN
	p[0] = (pixel >> 16) & 0xff;
	p[1] = (pixel >> 8) & 0xff;
	p[2] = pixel & 0xff;
	#else
	p[0] = pixel & 0xff;
	p[1] = (pixel >> 8) & 0xff;
	p[2] = (pixel >> 16) & 0xff;
	#endif
}
Uint32 get_pixel_rgb24(int x, int y, SDL_Surface* surface) {
	Uint8* pixels_pointer = (Uint8*)surface->pixels;
	int pitch = surface->pitch;
	Uint8* p = pixels_pointer + y*pitch + x*surface->format->BytesPerPixel;
	Uint32 result = 0;
	#if SDL_BYTEORDER==SDL_BIG_ENDIAN
	result += (p[0]&0xff)<<16;
	result += (p[1]&0xff)<<8;
	result += (p[2]&0xff);
	#else
	result += (p[0]&0xff);
	result += (p[1]&0xff)<<8;
	result += (p[2]&0xff)<<16;
	#endif
	return result;
}
Uint32 get_pixel_rgb32(int x, int y, SDL_Surface* surface) {
	Uint8* pixels_pointer = (Uint8*)surface->pixels;
	int pitch = surface->pitch;
	Uint8* p = pixels_pointer + y*pitch + x*surface->format->BytesPerPixel;
	return *(Uint32*)p;
}

DT_FP cubic_weight(DT_FP x) {
	static DT_FP A = (DT_FP)(-0.75);
	x = fabs(x);
	if (x<(DT_FP)(1.0)) {
		return (A+(DT_FP)(2.0))*x*x*x - (A+(DT_FP)(3.0))*x*x + (DT_FP)(1.0);
	}
	else if (x<(DT_FP)(2.0)) {
		return A*x*x*x - (DT_FP)(5.0)*A*x*x + (DT_FP)(8.0)*A*x - (DT_FP)(4.0)*A;
	}
	return (DT_FP)(0.0);
}
inline bool add_cubic_sample(DT_FP u, DT_FP v, DT_FP x, DT_FP y, SDL_Surface* texture, DT_FP result[3]) {
	glLib::Math::Vec::fillzeros3(result);
	DT_FP weight = cubic_weight(x)*cubic_weight(y);
	if (weight==(DT_FP)(0.0)) return false;
	sample_nearest(u,v,texture,result);
	glLib::Math::Vec::scale3(weight,result);
	return true;
}
void sample_cubic(DT_FP u, DT_FP v, SDL_Surface* texture, DT_FP result[3]) {
	//http://http.developer.nvidia.com/GPUGems/gpugems_ch24.html
	glLib::Math::Vec::fillzeros3(result);
	DT_FP fu = floor(u), fv = floor(v);
	for (int i=0;i<4;++i) {
		for (int j=0;j<4;++j) {
			DT_FP sample_x = fu - 1.0 + i;
			DT_FP sample_y = fv - 1.0 + j;
			DT_FP sample[3];
			if (add_cubic_sample(sample_x,sample_y, u-sample_x,v-sample_y, texture,sample)) {
				glLib::Math::Vec::add3(sample,result,result);
			}
		}
	}
}

void sample_bilinear(DT_FP u, DT_FP v, SDL_Surface* texture, DT_FP result[3]) {
	glLib::Math::Vec::fillzeros3(result);
	DT_FP result2[3],result3[3],result23[3], result4[3],result5[3],result45[3];

	sample_nearest(floor(u),floor(v),texture,result2);
	sample_nearest( ceil(u),floor(v),texture,result3);
	glLib::Math::Vec::scale3(ceil(u)-u, result2);
	glLib::Math::Vec::scale3(u-floor(u),result3);
	glLib::Math::Vec::add3(result2,result3,result23);

	sample_nearest(floor(u), ceil(v),texture,result4);
	sample_nearest( ceil(u), ceil(v),texture,result5);
	glLib::Math::Vec::scale3(ceil(u)-u, result4);
	glLib::Math::Vec::scale3(u-floor(u),result5);
	glLib::Math::Vec::add3(result4,result5,result45);

	glLib::Math::Vec::scale3(ceil(v)-v, result23);
	glLib::Math::Vec::scale3(v-floor(v),result45);
	glLib::Math::Vec::add3(result23,result45,result);
}

void sample_gauss(DT_FP u, DT_FP v, SDL_Surface* texture, DT_FP result[3], int radius) {
	static DT_FP recip_sqrt_two_pi_quantity_squared = pow(1.0/sqrt(2.0*glLib::Math::Misc::pi),2.0);
	glLib::Math::Vec::fillzeros3(result);
	double fu = floor(u), fv = floor(v);
	for (int i=-radius;i<=radius;++i) {
		for (int j=-radius;j<=radius;++j) {
			DT_FP sample_x = fu - (DT_FP)(1.0) + i;
			DT_FP sample_y = fv - (DT_FP)(1.0) + j;
			DT_FP sample[3];
			DT_FP weight = recip_sqrt_two_pi_quantity_squared*
				exp(-(u-sample_x)*(u-sample_x)*(DT_FP)(0.5))*
				exp(-(v-sample_y)*(v-sample_y)*(DT_FP)(0.5));
			sample_nearest(sample_x,sample_y,texture,sample);
			glLib::Math::Vec::scale3(weight,sample);
			glLib::Math::Vec::add3(sample,result,result);
		}
	}
}