kwin/blur: Refactor the gaussian kernel code

This commit is contained in:
Fredrik Höglund 2013-03-13 18:16:58 +01:00
parent 54308889f0
commit eff09c2b6f
2 changed files with 58 additions and 37 deletions

View file

@ -76,31 +76,38 @@ float BlurShader::gaussian(float x, float sigma) const
* std::exp(-((x * x) / (2.0 * sigma * sigma)));
}
QVector<float> BlurShader::gaussianKernel() const
QList<KernelValue> BlurShader::gaussianKernel() const
{
int size = qMin(mRadius | 1, maxKernelSize());
if (!(size & 0x1))
size -= 1;
QVector<float> kernel(size);
QList<KernelValue> kernel;
const int center = size / 2;
const qreal sigma = (size - 1) / 2.5;
// Generate the gaussian kernel
kernel[center] = gaussian(0, sigma) * .5;
for (int i = 1; i <= center; i++) {
const float val = gaussian(1.5 + (i - 1) * 2.0, sigma);
kernel[center + i] = val;
kernel[center - i] = val;
kernel << KernelValue(0.0, gaussian(0.0, sigma));
float total = kernel[0].g;
for (int x = 1; x <= center; x++) {
const float fx = (x - 1) * 2 + 1.5;
const float g1 = gaussian(fx - 0.5, sigma);
const float g2 = gaussian(fx + 0.5, sigma);
// Offset taking the contribution of both pixels into account
const float offset = .5 - g1 / (g1 + g2);
kernel << KernelValue(fx + offset, g1 + g2);
kernel << KernelValue(-(fx + offset), g1 + g2);
total += (g1 + g2) * 2;
}
// Normalize the kernel
qreal total = 0;
for (int i = 0; i < size; i++)
total += kernel[i];
qSort(kernel);
for (int i = 0; i < size; i++)
kernel[i] /= total;
// Normalize the kernel
for (int i = 0; i < kernel.count(); i++)
kernel[i].g /= total;
return kernel;
}
@ -219,13 +226,27 @@ int GLSLBlurShader::maxKernelSize() const
#endif
}
void GLSLBlurShader::init()
{
QVector<float> kernel = gaussianKernel();
QList<KernelValue> kernel = gaussianKernel();
const int size = kernel.size();
const int center = size / 2;
QList<QVector4D> offsets;
for (int i = 0; i < kernel.size(); i += 2) {
QVector4D vec4(0, 0, 0, 0);
vec4.setX(kernel[i].x);
vec4.setY(kernel[i].x);
if (i < kernel.size() - 1) {
vec4.setZ(kernel[i + 1].x);
vec4.setW(kernel[i + 1].x);
}
offsets << vec4;
}
QByteArray vertexSource;
QByteArray fragmentSource;
@ -244,20 +265,10 @@ void GLSLBlurShader::init()
stream << "{\n";
stream << " vec4 center = vec4(u_textureMatrix * texCoord).stst;\n";
stream << " vec4 ps = pixelSize.stst;\n\n";
for (int i = 0; i < size; i += 2) {
float offset1, offset2;
if (i < center) {
offset1 = -(1.5 + (center - i - 1) * 2.0);
offset2 = (i + 1) == center ? 0 : offset1 + 2;
} else if (i > center) {
offset1 = 1.5 + (i - center - 1) * 2.0;
offset2 = (i + 1) == size ? 0 : offset1 + 2;
} else {
offset1 = 0;
offset2 = 1.5;
}
stream << " samplePos[" << i / 2 << "] = center + ps * vec4("
<< offset1 << ", " << offset1 << ", " << offset2 << ", " << offset2 << ");\n";
for (int i = 0; i < offsets.size(); i++) {
stream << " samplePos[" << i << "] = center + ps * vec4("
<< offsets[i].x() << ", " << offsets[i].y() << ", "
<< offsets[i].z() << ", " << offsets[i].w() << ");\n";
}
stream << "\n";
stream << " gl_Position = u_modelViewProjectionMatrix * vertex;\n";
@ -272,14 +283,14 @@ void GLSLBlurShader::init()
stream2 << "varying vec4 samplePos[" << std::ceil(size / 2.0) << "];\n\n";
for (int i = 0; i <= center; i++)
stream2 << "const vec4 kernel" << i << " = vec4(" << kernel[i] << ");\n";
stream2 << "const float kernel" << i << " = " << kernel[i].g << ";\n";
stream2 << "\n";
stream2 << "void main(void)\n";
stream2 << "{\n";
stream2 << " vec4 sum = texture2D(texUnit, samplePos[0].st) * kernel0;\n";
for (int i = 1; i < size; i++)
stream2 << " sum = sum + texture2D(texUnit, samplePos[" << i / 2 << ((i % 2) ? "].pq)" : "].st)")
<< " * kernel" << (i > center ? size - i - 1 : i) << ";\n";
for (int i = 1, j = -center + 1; i < size; i++, j++)
stream2 << " sum = sum + texture2D(texUnit, samplePos[" << i / 2
<< ((i % 2) ? "].pq)" : "].st)") << " * kernel" << center - qAbs(j) << ";\n";
stream2 << " gl_FragColor = sum;\n";
stream2 << "}\n";
stream2.flush();
@ -409,7 +420,7 @@ int ARBBlurShader::maxKernelSize() const
void ARBBlurShader::init()
{
QVector<float> kernel = gaussianKernel();
QList<KernelValue> kernel = gaussianKernel();
const int size = kernel.size();
const int center = size / 2;
@ -420,7 +431,7 @@ void ARBBlurShader::init()
// The kernel values are hardcoded into the program
for (int i = 0; i <= center; i++)
stream << "PARAM kernel" << i << " = " << kernel[center + i] << ";\n";
stream << "PARAM kernel" << i << " = " << kernel[center + i].g << ";\n";
stream << "PARAM firstSample = program.local[0];\n"; // Distance from gl_TexCoord[0] to the next sample
stream << "PARAM nextSample = program.local[1];\n"; // Distance to the subsequent sample

View file

@ -27,6 +27,16 @@ class QMatrix4x4;
namespace KWin
{
struct KernelValue
{
KernelValue() {}
KernelValue(float x, float g) : x(x), g(g) {}
bool operator < (const KernelValue &other) const { return x < other.x; }
float x;
float g;
};
class BlurShader
{
public:
@ -61,7 +71,7 @@ public:
protected:
float gaussian(float x, float sigma) const;
QVector<float> gaussianKernel() const;
QList<KernelValue> gaussianKernel() const;
void setIsValid(bool value) {
mValid = value;
}