aboutsummaryrefslogblamecommitdiffstats
path: root/libglitter_redistribute_energy_double.c
blob: 24d6c4a8b9ca12e0f97c71200d33518671eb10b9 (plain) (tree)
1
2
3
4
5




                                                         




















                                                 
           
                                                                                                                         
 
                       
 
                                                                                                

                                                   
                                                                     
                                                     
                                                                     



                                                       

                                                         
                                               

                                                         






                                                           





                                                                                                       
                                                                

                                                                                                       

                                                  
         



           
                                                                                                                         
 
                       
 
                                                                                                

                                                     


                                                               
                         

                                                       









                                                                                             






                                                                                             
                 




         
                                                                                                           
                                                                                                                           
 
                              
                                                      
                                                                                 
                                          




                                                                                 


















                                            

                                               




















































































                                                                                         





































































































                                                                                         

                 



      
/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifndef TEST


#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
#elif defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wfloat-equal"
#endif

static int
exactly(double a, double b)
{
	return a == b;
}

#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#elif defined(__clang__)
# pragma clang diagnostic pop
#endif


static void
vconvolute(double *restrict raster, size_t rowsize, size_t width, size_t height, size_t kernelsize, const double *kernel)
{
	size_t y, x, i;

	if (kernelsize == 3 && exactly(kernel[0], kernel[1]) && exactly(kernel[1], kernel[2])) {
		for (y = 0; y < height; y++) {
			for (x = 0; x < width; x++)
				raster[x] += raster[x + 1 * rowsize];
			for (x = 0; x < width; x++) {
				raster[x] += raster[x + 2 * rowsize];
				raster[x] *= kernel[0];
			}
			raster = &raster[rowsize];
		}
		for (x = 0; x < width; x++) {
			raster[x] += raster[x + rowsize];
			raster[x] *= kernel[0];
			raster[x + rowsize] *= kernel[0];
		}

	} else {
		for (y = 0; y < height; y++) {
			for (x = 0; x < width; x++)
				raster[x] *= kernel[0];
			for (i = 1; i < kernelsize; i++)
				for (x = 0; x < width; x++)
					raster[x] = fma(raster[x + i * rowsize], kernel[i], raster[x]);
			raster = &raster[rowsize];
		}
		for (y = 0; y < kernelsize - 1; y++) {
			for (x = 0; x < width; x++)
				raster[x] *= kernel[0];
			for (i = 1; i < kernelsize - 1 - y; i++)
				for (x = 0; x < width; x++)
					raster[x] = fma(raster[x + i * rowsize], kernel[i], raster[x]);
			raster = &raster[rowsize];
		}
	}
}


static void
hconvolute(double *restrict raster, size_t rowsize, size_t width, size_t height, size_t kernelsize, const double *kernel)
{
	size_t y, x, i;

	if (kernelsize == 3 && exactly(kernel[0], kernel[1]) && exactly(kernel[1], kernel[2])) {
		for (y = 0; y < height; y++) {
			for (x = 0; x < width; x++) {
				raster[x + 1] += raster[x + 2];
				raster[x] += raster[x + 2];
				raster[x] *= kernel[0];
			}
			raster[width] *= kernel[0];
			raster[width + 1] *= kernel[0];
			raster = &raster[rowsize];
		}

	} else {
		for (y = 0; y < height; y++) {
			for (x = 0; x < width; x++) {
				raster[x] *= kernel[0];
				for (i = 1; i < kernelsize; i++)
					raster[x] = fma(raster[x + i], kernel[i], raster[x]);
			}
			raster = &raster[width];
			for (x = 0; x < kernelsize - 1; x++) {
				raster[x] *= kernel[0];
				for (i = 1; i < kernelsize - 1 - x; i++)
					raster[x] = fma(raster[x + i], kernel[i], raster[x]);
			}
			raster = &raster[rowsize - width];
		}
	}
}


void
libglitter_redistribute_energy_double(double *restrict raster, size_t rowsize, size_t width, size_t height,
                                      size_t hkernelsize, size_t vkernelsize, const double *hkernel, const double *vkernel)
{
	if (vkernelsize > 1) {
		raster -= (vkernelsize - 1) * rowsize;
		vconvolute(raster, rowsize, width, height, vkernelsize, vkernel);
		height += vkernelsize - 1;
	}
	if (hkernelsize > 1) {
		raster -= hkernelsize - 1;
		hconvolute(raster, rowsize, width, height, hkernelsize, hkernel);
		width += hkernelsize - 1;
	}
}


#else


#define TOLERANCE 0.0001

static int
eq(double a, double b)
{
	double r = a - b;
	return (r < 0 ? -r : r) < TOLERANCE;
}

int
main(void)
{
	double raster[100], hkern[5], vkern[5];
	size_t i, j;

	for (i = 0; i < sizeof(raster) / sizeof(*raster); i++)
		raster[i] = (double)i;
	hkern[0] = vkern[0] = 0;
	libglitter_redistribute_energy_double(&raster[11], 10, 7, 8, 1, 1, NULL, NULL);
	for (i = 0; i < sizeof(raster) / sizeof(*raster); i++)
		ASSERT(raster[i] == (double)i);
	libglitter_redistribute_energy_double(&raster[11], 10, 7, 8, 1, 1, hkern, NULL);
	for (i = 0; i < sizeof(raster) / sizeof(*raster); i++)
		ASSERT(raster[i] == (double)i);
	libglitter_redistribute_energy_double(&raster[11], 10, 7, 8, 1, 1, NULL, vkern);
	for (i = 0; i < sizeof(raster) / sizeof(*raster); i++)
		ASSERT(raster[i] == (double)i);
	libglitter_redistribute_energy_double(&raster[11], 10, 7, 8, 1, 1, hkern, vkern);
	for (i = 0; i < sizeof(raster) / sizeof(*raster); i++)
		ASSERT(raster[i] == (double)i);

	memset(raster, 0, sizeof(raster));
	raster[53] = 1;
	raster[57] = 2;
	hkern[0] = 1 / 3.;
	hkern[1] = 1 / 3.;
	hkern[2] = 1 / 3.;
	libglitter_redistribute_energy_double(&raster[12], 10, 7, 8, 3, 1, hkern, NULL);
	ASSERT(eq(raster[52 - 1], 1 / 3.));
	ASSERT(eq(raster[53 - 1], 1 / 3.));
	ASSERT(eq(raster[54 - 1], 1 / 3.));
	ASSERT(eq(raster[56 - 1], 2 / 3.));
	ASSERT(eq(raster[57 - 1], 2 / 3.));
	ASSERT(eq(raster[58 - 1], 2 / 3.));
	for (i = 0; i < sizeof(raster) / sizeof(*raster); i++)
		if (!(51 <= i && i <= 53) && !(55 <= i && i <= 57))
			ASSERT(!raster[i]);

	memset(raster, 0, sizeof(raster));
	raster[53] = 1;
	raster[57] = 2;
	hkern[0] = 1 / 4.;
	hkern[1] = 1 / 2.;
	hkern[2] = 1 / 4.;
	libglitter_redistribute_energy_double(&raster[12], 10, 7, 8, 3, 1, hkern, NULL);
	ASSERT(eq(raster[52 - 1], 1 / 4.));
	ASSERT(eq(raster[53 - 1], 1 / 2.));
	ASSERT(eq(raster[54 - 1], 1 / 4.));
	ASSERT(eq(raster[56 - 1], 2 / 4.));
	ASSERT(eq(raster[57 - 1], 2 / 2.));
	ASSERT(eq(raster[58 - 1], 2 / 4.));
	for (i = 0; i < sizeof(raster) / sizeof(*raster); i++)
		if (!(51 <= i && i <= 53) && !(55 <= i && i <= 57))
			ASSERT(!raster[i]);

	memset(raster, 0, sizeof(raster));
	raster[35] = 1;
	raster[75] = 2;
	vkern[0] = 1 / 3.;
	vkern[1] = 1 / 3.;
	vkern[2] = 1 / 3.;
	libglitter_redistribute_energy_double(&raster[21], 10, 8, 7, 1, 3, NULL, vkern);
	ASSERT(eq(raster[25 - 10], 1 / 3.));
	ASSERT(eq(raster[35 - 10], 1 / 3.));
	ASSERT(eq(raster[45 - 10], 1 / 3.));
	ASSERT(eq(raster[65 - 10], 2 / 3.));
	ASSERT(eq(raster[75 - 10], 2 / 3.));
	ASSERT(eq(raster[85 - 10], 2 / 3.));
	for (i = 0; i < sizeof(raster) / sizeof(*raster); i++)
		if (i != 15 && i != 25 && i != 35 && i != 55 && i != 65 && i != 75)
			ASSERT(!raster[i]);

	memset(raster, 0, sizeof(raster));
	raster[35] = 1;
	raster[75] = 2;
	vkern[0] = 1 / 4.;
	vkern[1] = 1 / 2.;
	vkern[2] = 1 / 4.;
	libglitter_redistribute_energy_double(&raster[21], 10, 8, 7, 1, 3, NULL, vkern);
	ASSERT(eq(raster[25 - 10], 1 / 4.));
	ASSERT(eq(raster[35 - 10], 1 / 2.));
	ASSERT(eq(raster[45 - 10], 1 / 4.));
	ASSERT(eq(raster[65 - 10], 2 / 4.));
	ASSERT(eq(raster[75 - 10], 2 / 2.));
	ASSERT(eq(raster[85 - 10], 2 / 4.));
	for (i = 0; i < sizeof(raster) / sizeof(*raster); i++)
		if (i != 15 && i != 25 && i != 35 && i != 55 && i != 65 && i != 75)
			ASSERT(!raster[i]);

	memset(raster, 0, sizeof(raster));
	for (j = 20; j < 100; j += 10)
		for (i = 2; i < 10; i += 1)
			raster[j + i] = 1;
	vkern[0] = 1 / 3.;
	vkern[1] = 1 / 3.;
	vkern[2] = 1 / 3.;
	hkern[0] = 1 / 3.;
	hkern[1] = 1 / 3.;
	hkern[2] = 1 / 3.;
	libglitter_redistribute_energy_double(&raster[22], 10, 8, 8, 3, 3, hkern, vkern);
	for (j = 0; j < 100; j += 10)
		for (i = 0; i < 10; i += 1)
			ASSERT(eq(raster[j + i],
			          ((i == 0 || i == 9) ? 1 / 3. :
			           (i == 1 || i == 8) ? 2 / 3. : 1.) *
			          ((j ==  0 || j == 90) ? 1 / 3. :
			           (j == 10 || j == 80) ? 2 / 3. : 1.)));

	memset(raster, 0, sizeof(raster));
	for (j = 20; j < 100; j += 10)
		for (i = 2; i < 10; i += 1)
			raster[j + i] = 1;
	vkern[0] = 1 / 3.;
	vkern[1] = 1 / 3.;
	vkern[2] = 1 / 3.;
	hkern[0] = 2 / 9.;
	hkern[1] = 5 / 9.;
	hkern[2] = 2 / 9.;
	libglitter_redistribute_energy_double(&raster[22], 10, 8, 8, 3, 3, hkern, vkern);
	for (j = 0; j < 100; j += 10)
		for (i = 0; i < 10; i += 1)
			ASSERT(eq(raster[j + i],
			          ((i == 0 || i == 9) ? 2 / 9. :
			           (i == 1 || i == 8) ? 7 / 9. : 1.) *
			          ((j ==  0 || j == 90) ? 1 / 3. :
			           (j == 10 || j == 80) ? 2 / 3. : 1.)));

	memset(raster, 0, sizeof(raster));
	for (j = 20; j < 100; j += 10)
		for (i = 2; i < 10; i += 1)
			raster[j + i] = 1;
	vkern[0] = 2 / 9.;
	vkern[1] = 5 / 9.;
	vkern[2] = 2 / 9.;
	hkern[0] = 1 / 3.;
	hkern[1] = 1 / 3.;
	hkern[2] = 1 / 3.;
	libglitter_redistribute_energy_double(&raster[22], 10, 8, 8, 3, 3, hkern, vkern);
	for (j = 0; j < 100; j += 10)
		for (i = 0; i < 10; i += 1)
			ASSERT(eq(raster[j + i],
			          ((i == 0 || i == 9) ? 1 / 3. :
			           (i == 1 || i == 8) ? 2 / 3. : 1.) *
			          ((j ==  0 || j == 90) ? 2 / 9. :
			           (j == 10 || j == 80) ? 7 / 9. : 1.)));

	memset(raster, 0, sizeof(raster));
	for (j = 20; j < 100; j += 10)
		for (i = 2; i < 10; i += 1)
			raster[j + i] = 1;
	vkern[0] = 2 / 9.;
	vkern[1] = 5 / 9.;
	vkern[2] = 2 / 9.;
	hkern[0] = 2 / 9.;
	hkern[1] = 5 / 9.;
	hkern[2] = 2 / 9.;
	libglitter_redistribute_energy_double(&raster[22], 10, 8, 8, 3, 3, hkern, vkern);
	for (j = 0; j < 100; j += 10)
		for (i = 0; i < 10; i += 1)
			ASSERT(eq(raster[j + i],
			          ((i == 0 || i == 9) ? 2 / 9. :
			           (i == 1 || i == 8) ? 7 / 9. : 1.) *
			          ((j ==  0 || j == 90) ? 2 / 9. :
			           (j == 10 || j == 80) ? 7 / 9. : 1.)));

	memset(raster, 0, sizeof(raster));
	for (j = 40; j < 100; j += 10)
		for (i = 4; i < 10; i += 1)
			raster[j + i] = 1;
	vkern[0] = 1 / 5.;
	vkern[1] = 1 / 5.;
	vkern[2] = 1 / 5.;
	vkern[3] = 1 / 5.;
	vkern[4] = 1 / 5.;
	hkern[0] = 1 / 9.;
	hkern[1] = 2 / 9.;
	hkern[2] = 3 / 9.;
	hkern[3] = 2 / 9.;
	hkern[4] = 1 / 9.;
	libglitter_redistribute_energy_double(&raster[44], 10, 6, 6, 5, 5, hkern, vkern);
	for (j = 0; j < 100; j += 10)
		for (i = 0; i < 10; i += 1)
			ASSERT(eq(raster[j + i],
			          ((i == 0 || i == 9) ? 1 / 9. :
			           (i == 1 || i == 8) ? 3 / 9. :
			           (i == 2 || i == 7) ? 6 / 9. :
			           (i == 3 || i == 6) ? 8 / 9. : 1.) *
			          ((j ==  0 || j == 90) ? 1 / 5. :
			           (j == 10 || j == 80) ? 2 / 5. :
			           (j == 20 || j == 70) ? 3 / 5. :
			           (j == 30 || j == 60) ? 4 / 5. : 1.)));

	return 0;
}


#endif