diff --git a/effects/blur/blurshader.cpp b/effects/blur/blurshader.cpp index 6810b8f56d..f2fa067f15 100644 --- a/effects/blur/blurshader.cpp +++ b/effects/blur/blurshader.cpp @@ -76,31 +76,38 @@ float BlurShader::gaussian(float x, float sigma) const * std::exp(-((x * x) / (2.0 * sigma * sigma))); } -QVector BlurShader::gaussianKernel() const +QList BlurShader::gaussianKernel() const { int size = qMin(mRadius | 1, maxKernelSize()); if (!(size & 0x1)) size -= 1; - QVector kernel(size); + QList 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 kernel = gaussianKernel(); + QList kernel = gaussianKernel(); const int size = kernel.size(); const int center = size / 2; + QList 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 kernel = gaussianKernel(); + QList 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 diff --git a/effects/blur/blurshader.h b/effects/blur/blurshader.h index 388f156e3a..5d65b4d3ef 100644 --- a/effects/blur/blurshader.h +++ b/effects/blur/blurshader.h @@ -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 gaussianKernel() const; + QList gaussianKernel() const; void setIsValid(bool value) { mValid = value; }