Plan 9 from Bell Labs’s /usr/web/sources/contrib/pac/sys/src/ape/cmd/img/refocit-2.0.0/blur.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * Written 2003 Lukas Kunc <Lukas.Kunc@seznam.cz>
 * Code for calculation of circle intensity (C) 1999-2003 Ernst Lippe 
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <math.h>
#include <stdlib.h>
#include "blur.h"

#ifndef SQR
#define SQR(x) ((x)*(x))
#endif

typedef struct
{
	real_t x;
	real_t y;
} point_t;


static double circle_integral (double x, double radius)
/* Return the integral of sqrt(radius^2 - z^2) for z = 0 to x */
{
	double sin, sq_diff;
	if (radius < 1e-6)
	{
		return (0.0);
	}
	else
	{
		sin = x / radius;
		sq_diff = SQR (radius) - SQR (x);
		/* From a mathematical point of view the following is redundant.
		Numerically they are not equivalent!
		*/
		if ((sq_diff < 0.0) || (sin < -1.0) || (sin > 1.0))
		{
			if (sin < 0.0)
			{
				return (-0.25 * SQR (radius) * M_PI);
			}
			else
			{
				return (0.25 * SQR (radius) * M_PI);
			}
		}
		else
		{
			return (0.5 * x * sqrt (sq_diff) + 0.5 * SQR (radius) * asin (sin));
		}
	}
}

static double circle_intensity (int x, int y, double radius)
{
	double xlo, xhi, ylo, yhi, xc1, xc2;
	double symmetry_factor;

	if (radius <= 1e-4)
	{
		return (((x == 0) && (y == 0)) ? 1.0 : 0.0);
	}
	else
	{
		xlo = abs (x) - 0.5;
		xhi = abs (x) + 0.5;
		ylo = abs (y) - 0.5;
		yhi = abs (y) + 0.5;
		symmetry_factor = 1.0;

		if (xlo < 0.0)
		{
			xlo = 0.0;
			symmetry_factor *= 2.0;
		};
		if (ylo < 0.0)
		{
			ylo = 0.0;
			symmetry_factor *= 2.0;
		};
		if (SQR (xlo) + SQR (yhi) > SQR (radius))
		{
			xc1 = xlo;
		}
		else if (SQR (xhi) + SQR (yhi) > SQR (radius))
		{
			xc1 = sqrt (SQR (radius) - SQR (yhi));
		}
		else
		{
			xc1 = xhi;
		};
		if (SQR (xlo) + SQR (ylo) > SQR (radius))
		{
			xc2 = xlo;
		}
		else if (SQR (xhi) + SQR (ylo) > SQR (radius))
		{
			xc2 = sqrt (SQR (radius) - SQR (ylo));
		}
		else
		{
			xc2 = xhi;
		};
		return (((yhi - ylo) * (xc1 - xlo) +
			circle_integral (xc2, radius) - circle_integral (xc1, radius) -
			(xc2 - xc1) * ylo) * symmetry_factor / (M_PI * SQR (radius)));
	}
}


/*
 * Create the convolution mask for out-of-focus blur.
 */
convmask_t* blur_create_defocus(convmask_t* blur, real_t radius)
{
	int i, j, r;
	real_t val;

	r = (int)(radius + R(0.5));
	convmask_create(blur, r);

	if (r < 1)
	{
		convmask_set(blur, 0, 0, R(1.0));
		return blur;
	}
	else
	{
		for (i = 0; i <= r; i++) {
			for (j = 0; j <= r; j++) {
				val = (real_t)circle_intensity(i, j, radius);
				if (val < R(0.0)) val = R(0.0);
				convmask_set_circle(blur, i, j, val);
			}
		}
		return convmask_normalize(blur);
	}
}

/*
 * Create the convolution mask for gaussian blur.
 */
convmask_t* blur_create_gauss(convmask_t* blur, real_t variance)
{
	double epsilon;
	double mult;
	double var;
	int i, j, radius;

	if (variance < R(1e-6))
	{
		convmask_create(blur, 0);
		convmask_set(blur, 0, 0, R(1.0));
		return blur;
	}
	else
	{
		/*
		 * Compute the radius such that the magnitude of central element
		 * is at least 2 times the magnitude of outermost elements.
		 */
		var = (double)variance;
		epsilon = sqrt(-2.0 * log(1e-2));
		radius = (int)(var * epsilon + 0.5);
		convmask_create(blur, radius);
		var *= var * 2.0;
		mult = var * M_PI;
		for (i = 0; i <= radius; i++) {
			for (j = 0; j <= radius; j++) {
				convmask_set_circle(blur, i, j, (real_t)(exp(-((double)(i*i + j*j)) / var) / mult));
			}
		}
		return convmask_normalize(blur);
	}
}

static void make_coords_line(point_t* c, real_t r, double a)
{
	real_t sina, cosa;
	real_t x, y;
	int i;

	a *= M_PI / 180.0;
	sina = (real_t)sin(a);
	cosa = (real_t)cos(a);
	c[0].x = -R(0.5);
	c[0].y = R(0.5);
	c[1].x = -R(0.5);
	c[1].y = -R(0.5);
	c[2].x = r + R(0.5);
	c[2].y = -R(0.5);
	c[3].x = r + R(0.5);
	c[3].y = R(0.5);

	for (i = 0; i < 4; i++)
	{
		x = cosa * c[i].x - sina * c[i].y;
		y = sina * c[i].x + cosa * c[i].y;
		c[i].x = x;
		c[i].y = y;
	}
}

static void isect(point_t* isc, point_t* a, point_t* b, real_t x)
{
	real_t t;
	t = (x - a->x) / (b->x - a->x);
	isc->y = -x;
	isc->x = a->y + t * (b->y - a->y);
}

static int clip(point_t* dst, point_t* src, int n, real_t max)
{
	int prev, in, on, i;

	prev = n-1;
	in = (src[prev].x <= max + 1e-4);
	on = 0;

	for (i = 0; i < n; prev = i++)
	{
		if (src[i].x <= max + 1e-4)
		{
			/* current is in */
			if (!in)
			{
				/* previous was out - need to compute intersection */
				isect(dst + (on++), src + i, src + prev, max);
			}
			dst[on].x = src[i].y;
			dst[on++].y = -src[i].x;
			in = 1;
		}
		else
		{
			/* current is out */
			if (in)
			{
				isect(dst + (on++), src + prev, src + i, max);
				/* previous was in - need to compute intersection */
			}
			in = 0;
		}
	}
	return on;
}

/*
 * find intersection of the unit square with center at [x,y] and the rectangle in src
 */
static int find_intersect(point_t* dst, point_t* src, real_t x, real_t y)
{
	int n;
	point_t p[8];

	/* initially there are 4 points in the polygon being clipped */
	n = 4;

	/* clip against x + 0.5 */
	n = clip(p, src, n, x + R(0.5));
	if (n <= 0) return 0;

	/* clip against y + 0.5 */
	n = clip(dst, p, n, y + R(0.5));
	if (n <= 0) return 0;

	/* clip against x - 0.5 */
	n = clip(p, dst, n, -(x - R(0.5)));
	if (n <= 0) return 0;

	/* clip against y - 0.5 */
	n = clip(dst, p, n, -(y - R(0.5)));
	return n;
}

static real_t compute_area(point_t* poly, int n)
{
	real_t sum;
	int prev, i;

	sum = R(0.0);
	for (i = 0, prev = n-1; i < n; prev=i++)
	{
		sum += (poly[i].x - poly[prev].x) * (poly[i].y + poly[prev].y);
	}
	if (sum < R(0.0)) sum = -sum;
	return sum / R(2.0);
}

static real_t intersect_area(real_t x, real_t y, point_t* c)
{
	point_t isec[8];
	int verts;
	verts = find_intersect(isec, c, x, y);
	return compute_area(isec, verts);
}

/*
 * Create the convolution mask for motion blur.
 */
convmask_t* blur_create_motion(convmask_t* blur, real_t radius, real_t angle)
{
	int i, j, r;
	point_t coords_line[4];

	if (radius < R(1e-4))
	{
		convmask_create(blur, 0.01);
		convmask_set(blur, 0, 0, R(1.0));
		return blur;
	}
	else
	{
		r = (int)(radius + R(1.0));
		convmask_create(blur, r);
		make_coords_line(coords_line, radius, angle);
		for (i = -r; i <= r; i++) {
			for (j = -r; j <= r; j++) {
				convmask_set(blur, i, j, intersect_area((real_t)i, (real_t)j, coords_line));
			}
		}
		return convmask_normalize(blur);
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.