http://bugs.gentoo.org/306661
backport shader based upscalers from svn trunk
--- language/English/strings.xml
+++ language/English/strings.xml
@@ -1554,16 +1554,17 @@
Lanczos2
Lanczos3
Sinc8
-
Bicubic (software)
Lanczos (software)
Sinc (software)
-
(VDPAU)Temporal
(VDPAU)Temporal/Spatial
(VDPAU)Noise Reduction
(VDPAU)Sharpness
Inverse Telecine
+ Lanczos3 optimized
+ Auto
+
Display sleep timeout
Switch to channel
--- system/shaders/convolution-6x6.glsl
+++ system/shaders/convolution-6x6.glsl
@@ -0,0 +1,69 @@
+uniform sampler2D img;
+uniform float stepx;
+uniform float stepy;
+
+#if (HAS_FLOAT_TEXTURE)
+uniform sampler1D kernelTex;
+
+vec3 weight(float pos)
+{
+ return texture1D(kernelTex, pos).rgb;
+}
+#else
+uniform sampler2D kernelTex;
+
+vec3 weight(float pos)
+{
+ //row 0 contains the high byte, row 1 contains the low byte
+ return ((texture2D(kernelTex, vec2(pos, 0.0)) * 256.0 + texture2D(kernelTex, vec2(pos, 1.0)))).rgb / 128.5 - 1.0;
+}
+#endif
+
+vec3 pixel(float xpos, float ypos)
+{
+ return texture2D(img, vec2(xpos, ypos)).rgb;
+}
+
+vec3 line (float ypos, vec3 xpos1, vec3 xpos2, vec3 linetaps1, vec3 linetaps2)
+{
+ vec3 pixels;
+
+ pixels = pixel(xpos1.r, ypos) * linetaps1.r;
+ pixels += pixel(xpos1.g, ypos) * linetaps2.r;
+ pixels += pixel(xpos1.b, ypos) * linetaps1.g;
+ pixels += pixel(xpos2.r, ypos) * linetaps2.g;
+ pixels += pixel(xpos2.g, ypos) * linetaps1.b;
+ pixels += pixel(xpos2.b, ypos) * linetaps2.b;
+
+ return pixels;
+}
+
+void main()
+{
+ float xf = fract(gl_TexCoord[0].x / stepx);
+ float yf = fract(gl_TexCoord[0].y / stepy);
+
+ vec3 linetaps1 = weight((1.0 - xf) / 2.0);
+ vec3 linetaps2 = weight((1.0 - xf) / 2.0 + 0.5);
+ vec3 columntaps1 = weight((1.0 - yf) / 2.0);
+ vec3 columntaps2 = weight((1.0 - yf) / 2.0 + 0.5);
+
+ vec3 xpos1 = vec3(
+ (-1.5 - xf) * stepx + gl_TexCoord[0].x,
+ (-0.5 - xf) * stepx + gl_TexCoord[0].x,
+ ( 0.5 - xf) * stepx + gl_TexCoord[0].x);
+ vec3 xpos2 = vec3(
+ ( 1.5 - xf) * stepx + gl_TexCoord[0].x,
+ ( 2.5 - xf) * stepx + gl_TexCoord[0].x,
+ ( 3.5 - xf) * stepx + gl_TexCoord[0].x);
+
+ gl_FragColor.rgb = line((-1.5 - yf) * stepy + gl_TexCoord[0].y, xpos1, xpos2, linetaps1, linetaps2) * columntaps1.r;
+ gl_FragColor.rgb += line((-0.5 - yf) * stepy + gl_TexCoord[0].y, xpos1, xpos2, linetaps1, linetaps2) * columntaps2.r;
+ gl_FragColor.rgb += line(( 0.5 - yf) * stepy + gl_TexCoord[0].y, xpos1, xpos2, linetaps1, linetaps2) * columntaps1.g;
+ gl_FragColor.rgb += line(( 1.5 - yf) * stepy + gl_TexCoord[0].y, xpos1, xpos2, linetaps1, linetaps2) * columntaps2.g;
+ gl_FragColor.rgb += line(( 2.5 - yf) * stepy + gl_TexCoord[0].y, xpos1, xpos2, linetaps1, linetaps2) * columntaps1.b;
+ gl_FragColor.rgb += line(( 3.5 - yf) * stepy + gl_TexCoord[0].y, xpos1, xpos2, linetaps1, linetaps2) * columntaps2.b;
+
+ gl_FragColor.a = gl_Color.a;
+}
+
--- system/shaders/bicubic.glsl
+++ system/shaders/bicubic.glsl
@@ -0,0 +1,47 @@
+uniform sampler2D img;
+uniform float stepx;
+uniform float stepy;
+uniform sampler2D kernelTex;
+
+vec4 cubicFilter(float xValue, vec4 c0, vec4 c1, vec4 c2, vec4 c3)
+{
+ vec4 h = texture2D(kernelTex, vec2(xValue, 0.5));
+ vec4 r = c0 * h.r;
+ r += c1 * h.g;
+ r += c2 * h.b;
+ r += c3 * h.a;
+ return r;
+}
+
+void main()
+{
+ vec2 f = vec2(gl_TexCoord[0].x / stepx , gl_TexCoord[0].y / stepy);
+ f = fract(f);
+ vec4 t0 = cubicFilter(f.x,
+ texture2D(img, gl_TexCoord[0].xy + vec2(-stepx, -stepy)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(0.0, -stepy)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(stepx, -stepy)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(2.0*stepx, -stepy)));
+
+ vec4 t1 = cubicFilter(f.x,
+ texture2D(img, gl_TexCoord[0].xy + vec2(-stepx, 0.0)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(0.0, 0.0)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(stepx, 0.0)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(2.0*stepx, 0.0)));
+
+ vec4 t2 = cubicFilter(f.x,
+ texture2D(img, gl_TexCoord[0].xy + vec2(-stepx, stepy)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(0.0, stepy)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(stepx, stepy)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(2.0*stepx, stepy)));
+
+ vec4 t3 = cubicFilter(f.x,
+ texture2D(img, gl_TexCoord[0].xy + vec2(-stepx, 2.0*stepy)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(0, 2.0*stepy)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(stepx, 2.0*stepy)),
+ texture2D(img, gl_TexCoord[0].xy + vec2(2.0*stepx, 2.0*stepy)));
+
+ gl_FragColor = cubicFilter(f.y, t0, t1, t2, t3);
+ gl_FragColor.a = gl_Color.a;
+}
+
--- system/shaders/convolution-4x4.glsl
+++ system/shaders/convolution-4x4.glsl
@@ -0,0 +1,60 @@
+uniform sampler2D img;
+uniform float stepx;
+uniform float stepy;
+
+#if (HAS_FLOAT_TEXTURE)
+uniform sampler1D kernelTex;
+
+vec4 weight(float pos)
+{
+ return texture1D(kernelTex, pos);
+}
+#else
+uniform sampler2D kernelTex;
+
+vec4 weight(float pos)
+{
+ //row 0 contains the high byte, row 1 contains the low byte
+ return (texture2D(kernelTex, vec2(pos, 0.0)) * 256.0 + texture2D(kernelTex, vec2(pos, 1.0))) / 128.5 - 1.0;
+}
+#endif
+
+vec3 pixel(float xpos, float ypos)
+{
+ return texture2D(img, vec2(xpos, ypos)).rgb;
+}
+
+vec3 line (float ypos, vec4 xpos, vec4 linetaps)
+{
+ vec3 pixels;
+
+ pixels = pixel(xpos.r, ypos) * linetaps.r;
+ pixels += pixel(xpos.g, ypos) * linetaps.g;
+ pixels += pixel(xpos.b, ypos) * linetaps.b;
+ pixels += pixel(xpos.a, ypos) * linetaps.a;
+
+ return pixels;
+}
+
+void main()
+{
+ float xf = fract(gl_TexCoord[0].x / stepx);
+ float yf = fract(gl_TexCoord[0].y / stepy);
+
+ vec4 linetaps = weight(1.0 - xf);
+ vec4 columntaps = weight(1.0 - yf);
+
+ vec4 xpos = vec4(
+ (-0.5 - xf) * stepx + gl_TexCoord[0].x,
+ ( 0.5 - xf) * stepx + gl_TexCoord[0].x,
+ ( 1.5 - xf) * stepx + gl_TexCoord[0].x,
+ ( 2.5 - xf) * stepx + gl_TexCoord[0].x);
+
+ gl_FragColor.rgb = line((-0.5 - yf) * stepy + gl_TexCoord[0].y, xpos, linetaps) * columntaps.r;
+ gl_FragColor.rgb += line(( 0.5 - yf) * stepy + gl_TexCoord[0].y, xpos, linetaps) * columntaps.g;
+ gl_FragColor.rgb += line(( 1.5 - yf) * stepy + gl_TexCoord[0].y, xpos, linetaps) * columntaps.b;
+ gl_FragColor.rgb += line(( 2.5 - yf) * stepy + gl_TexCoord[0].y, xpos, linetaps) * columntaps.a;
+
+ gl_FragColor.a = gl_Color.a;
+}
+
--- xbmc/settings/VideoSettings.h
+++ xbmc/settings/VideoSettings.h
@@ -51,9 +51,10 @@
{
VS_SCALINGMETHOD_NEAREST=0,
VS_SCALINGMETHOD_LINEAR,
-
+
VS_SCALINGMETHOD_CUBIC,
VS_SCALINGMETHOD_LANCZOS2,
+ VS_SCALINGMETHOD_LANCZOS3_FAST,
VS_SCALINGMETHOD_LANCZOS3,
VS_SCALINGMETHOD_SINC8,
VS_SCALINGMETHOD_NEDI,
@@ -61,7 +62,9 @@
VS_SCALINGMETHOD_BICUBIC_SOFTWARE,
VS_SCALINGMETHOD_LANCZOS_SOFTWARE,
VS_SCALINGMETHOD_SINC_SOFTWARE,
- VS_SCALINGMETHOD_VDPAU_HARDWARE
+ VS_SCALINGMETHOD_VDPAU_HARDWARE,
+
+ VS_SCALINGMETHOD_AUTO
};
class CVideoSettings
--- xbmc/cores/VideoRenderers/VideoShaders/VideoFilterShader.cpp
+++ xbmc/cores/VideoRenderers/VideoShaders/VideoFilterShader.cpp
@@ -21,6 +21,7 @@
#include "system.h"
#include "VideoFilterShader.h"
#include "utils/log.h"
+#include "ConvolutionKernels.h"
#include
#include
@@ -63,60 +64,13 @@
BicubicFilterShader::BicubicFilterShader(float B, float C)
{
- string shaderf =
- "uniform sampler2D img;"
- "uniform float stepx;"
- "uniform float stepy;"
- "uniform sampler2D kernelTex;"
-
- "vec4 cubicFilter(float xValue, vec4 c0, vec4 c1, vec4 c2, vec4 c3)"
- "{"
- " vec4 h = texture2D(kernelTex, vec2(xValue, 0.5));"
- " vec4 r = c0 * h.r;"
- " r += c1 * h.g;"
- " r += c2 * h.b;"
- " r += c3 * h.a;"
- " return r;"
- "}"
- ""
- "void main()"
- "{"
- "vec2 f = vec2(gl_TexCoord[0].x / stepx , gl_TexCoord[0].y / stepy);"
- "f = fract(f);"
- "vec4 t0 = cubicFilter(f.x,"
- "texture2D(img, gl_TexCoord[0].xy + vec2(-stepx, -stepy)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(0.0, -stepy)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(stepx, -stepy)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(2.0*stepx, -stepy)));"
- ""
- "vec4 t1 = cubicFilter(f.x,"
- "texture2D(img, gl_TexCoord[0].xy + vec2(-stepx, 0.0)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(0.0, 0.0)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(stepx, 0.0)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(2.0*stepx, 0.0)));"
- ""
- "vec4 t2 = cubicFilter(f.x,"
- "texture2D(img, gl_TexCoord[0].xy + vec2(-stepx, stepy)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(0.0, stepy)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(stepx, stepy)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(2.0*stepx, stepy)));"
- ""
- "vec4 t3 = cubicFilter(f.x,"
- "texture2D(img, gl_TexCoord[0].xy + vec2(-stepx, 2.0*stepy)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(0, 2.0*stepy)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(stepx, 2.0*stepy)),"
- "texture2D(img, gl_TexCoord[0].xy + vec2(2.0*stepx, 2.0*stepy)));"
-
- "gl_FragColor = cubicFilter(f.y, t0, t1, t2, t3) ;"
- "gl_FragColor.a = gl_Color.a;"
- "}";
- PixelShader()->SetSource(shaderf);
+ PixelShader()->LoadSource("bicubic.glsl");
m_kernelTex1 = 0;
m_B = B;
m_C = C;
- if (B<=0)
+ if (B<0)
m_B=1.0f/3.0f;
- if (C<=0)
+ if (C<0)
m_C=1.0f/3.0f;
}
@@ -209,8 +163,8 @@
glBindTexture(GL_TEXTURE_2D, m_kernelTex1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, size, 1, 0, GL_RGBA, GL_FLOAT, img);
glActiveTexture(GL_TEXTURE0);
@@ -254,4 +208,110 @@
return val;
}
+ConvolutionFilterShader::ConvolutionFilterShader(ESCALINGMETHOD method)
+{
+ m_method = method;
+ m_kernelTex1 = 0;
+
+ string shadername;
+ string defines;
+
+ if (m_method == VS_SCALINGMETHOD_CUBIC ||
+ m_method == VS_SCALINGMETHOD_LANCZOS2 ||
+ m_method == VS_SCALINGMETHOD_LANCZOS3_FAST)
+ shadername = "convolution-4x4.glsl";
+ else if (m_method == VS_SCALINGMETHOD_LANCZOS3)
+ shadername = "convolution-6x6.glsl";
+
+ m_floattex = glewIsSupported("GL_ARB_texture_float");
+
+ if (m_floattex)
+ defines = "#define HAS_FLOAT_TEXTURE 1\n";
+ else
+ defines = "#define HAS_FLOAT_TEXTURE 0\n";
+
+ CLog::Log(LOGDEBUG, "GL: ConvolutionFilterShader: using %s defines: %s", shadername.c_str(), defines.c_str());
+ PixelShader()->LoadSource(shadername, defines);
+}
+
+void ConvolutionFilterShader::OnCompiledAndLinked()
+{
+ // obtain shader attribute handles on successfull compilation
+ m_hSourceTex = glGetUniformLocation(ProgramHandle(), "img");
+ m_hStepX = glGetUniformLocation(ProgramHandle(), "stepx");
+ m_hStepY = glGetUniformLocation(ProgramHandle(), "stepy");
+ m_hKernTex = glGetUniformLocation(ProgramHandle(), "kernelTex");
+
+ CConvolutionKernel kernel(m_method, 256);
+
+ if (m_kernelTex1)
+ {
+ glDeleteTextures(1, &m_kernelTex1);
+ m_kernelTex1 = 0;
+ }
+
+ glGenTextures(1, &m_kernelTex1);
+
+ if ((m_kernelTex1<=0))
+ {
+ CLog::Log(LOGERROR, "GL: ConvolutionFilterShader: Error creating kernel texture");
+ return;
+ }
+
+ glActiveTexture(GL_TEXTURE2);
+
+ //if float textures are supported, we can load the kernel as a 1d float texture
+ //if not, we load it as a 2d texture with 2 rows, where row 0 contains the high byte
+ //and row 1 contains the low byte, which can be converted in the shader
+ if (m_floattex)
+ {
+ glBindTexture(GL_TEXTURE_1D, m_kernelTex1);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16F_ARB, kernel.GetSize(), 0, GL_RGBA, GL_FLOAT, kernel.GetFloatPixels());
+ }
+ else
+ {
+ glBindTexture(GL_TEXTURE_2D, m_kernelTex1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kernel.GetSize(), 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kernel.GetIntFractPixels());
+ }
+
+ glActiveTexture(GL_TEXTURE0);
+
+ VerifyGLState();
+}
+
+bool ConvolutionFilterShader::OnEnabled()
+{
+ // set shader attributes once enabled
+ glActiveTexture(GL_TEXTURE2);
+
+ if (m_floattex)
+ glBindTexture(GL_TEXTURE_1D, m_kernelTex1);
+ else
+ glBindTexture(GL_TEXTURE_2D, m_kernelTex1);
+
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(m_hSourceTex, m_sourceTexUnit);
+ glUniform1i(m_hKernTex, 2);
+ glUniform1f(m_hStepX, m_stepX);
+ glUniform1f(m_hStepY, m_stepY);
+ VerifyGLState();
+ return true;
+}
+
+void ConvolutionFilterShader::Free()
+{
+ if (m_kernelTex1)
+ glDeleteTextures(1, &m_kernelTex1);
+ m_kernelTex1 = 0;
+ BaseVideoFilterShader::Free();
+}
+
#endif
--- xbmc/cores/VideoRenderers/VideoShaders/ConvolutionKernels.cpp
+++ xbmc/cores/VideoRenderers/VideoShaders/ConvolutionKernels.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2005-2008 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#ifdef _WIN32
+ #define _USE_MATH_DEFINES
+#endif
+
+#include "ConvolutionKernels.h"
+#include "MathUtils.h"
+
+#define SINC(x) (sin(M_PI * (x)) / (M_PI * (x)))
+
+CConvolutionKernel::CConvolutionKernel(ESCALINGMETHOD method, int size)
+{
+ m_size = size;
+ m_floatpixels = new float[m_size * 4];
+
+ if (method == VS_SCALINGMETHOD_LANCZOS2)
+ Lanczos2();
+ else if (method == VS_SCALINGMETHOD_LANCZOS3_FAST)
+ Lanczos3Fast();
+ else if (method == VS_SCALINGMETHOD_LANCZOS3)
+ Lanczos3();
+ else if (method == VS_SCALINGMETHOD_CUBIC)
+ Bicubic(1.0 / 3.0, 1.0 / 3.0);
+
+ ToIntFract();
+}
+
+CConvolutionKernel::~CConvolutionKernel()
+{
+ delete [] m_floatpixels;
+ delete [] m_intfractpixels;
+}
+
+//generate a lanczos2 kernel which can be loaded with RGBA format
+//each value of RGBA has one tap, so a shader can load 4 taps with a single pixel lookup
+void CConvolutionKernel::Lanczos2()
+{
+ for (int i = 0; i < m_size; i++)
+ {
+ double x = (double)i / (double)m_size;
+
+ //generate taps
+ for (int j = 0; j < 4; j++)
+ m_floatpixels[i * 4 + j] = (float)LanczosWeight(x + (double)(j - 2), 2.0);
+
+ //any collection of 4 taps added together needs to be exactly 1.0
+ //for lanczos this is not always the case, so we take each collection of 4 taps
+ //and divide those taps by the sum of the taps
+ float weight = 0.0;
+ for (int j = 0; j < 4; j++)
+ weight += m_floatpixels[i * 4 + j];
+
+ for (int j = 0; j < 4; j++)
+ m_floatpixels[i * 4 + j] /= weight;
+ }
+}
+
+//generate a lanczos3 kernel which can be loaded with RGBA format
+//each value of RGBA has one tap, so a shader can load 4 taps with a single pixel lookup
+//the two outer lobes of the lanczos3 kernel are added to the two lobes one step to the middle
+//this basically looks the same as lanczos3, but the kernel only has 4 taps,
+//so it can use the 4x4 convolution shader which is twice as fast as the 6x6 one
+void CConvolutionKernel::Lanczos3Fast()
+{
+ for (int i = 0; i < m_size; i++)
+ {
+ double a = 3.0;
+ double x = (double)i / (double)m_size;
+
+ //generate taps
+ m_floatpixels[i * 4 + 0] = (float)(LanczosWeight(x - 2.0, a) + LanczosWeight(x - 3.0, a));
+ m_floatpixels[i * 4 + 1] = (float) LanczosWeight(x - 1.0, a);
+ m_floatpixels[i * 4 + 2] = (float) LanczosWeight(x , a);
+ m_floatpixels[i * 4 + 3] = (float)(LanczosWeight(x + 1.0, a) + LanczosWeight(x + 2.0, a));
+
+ //any collection of 4 taps added together needs to be exactly 1.0
+ //for lanczos this is not always the case, so we take each collection of 4 taps
+ //and divide those taps by the sum of the taps
+ float weight = 0.0;
+ for (int j = 0; j < 4; j++)
+ weight += m_floatpixels[i * 4 + j];
+
+ for (int j = 0; j < 4; j++)
+ m_floatpixels[i * 4 + j] /= weight;
+ }
+}
+
+//generate a lanczos3 kernel which can be loaded with RGBA format
+//each value of RGB has one tap, so a shader can load 3 taps with a single pixel lookup
+void CConvolutionKernel::Lanczos3()
+{
+ for (int i = 0; i < m_size; i++)
+ {
+ double x = (double)i / (double)m_size;
+
+ //generate taps
+ for (int j = 0; j < 3; j++)
+ m_floatpixels[i * 4 + j] = (float)LanczosWeight(x * 2.0 + (double)(j * 2 - 3), 3.0);
+
+ m_floatpixels[i * 4 + 3] = 0.0;
+ }
+
+ //any collection of 6 taps added together needs to be exactly 1.0
+ //for lanczos this is not always the case, so we take each collection of 6 taps
+ //and divide those taps by the sum of the taps
+ for (int i = 0; i < m_size / 2; i++)
+ {
+ float weight = 0.0;
+ for (int j = 0; j < 3; j++)
+ {
+ weight += m_floatpixels[i * 4 + j];
+ weight += m_floatpixels[(i + m_size / 2) * 4 + j];
+ }
+ for (int j = 0; j < 3; j++)
+ {
+ m_floatpixels[i * 4 + j] /= weight;
+ m_floatpixels[(i + m_size / 2) * 4 + j] /= weight;
+ }
+ }
+}
+
+//generate a bicubic kernel which can be loaded with RGBA format
+//each value of RGBA has one tap, so a shader can load 4 taps with a single pixel lookup
+void CConvolutionKernel::Bicubic(double B, double C)
+{
+ for (int i = 0; i < m_size; i++)
+ {
+ double x = (double)i / (double)m_size;
+
+ //generate taps
+ for (int j = 0; j < 4; j++)
+ m_floatpixels[i * 4 + j] = (float)BicubicWeight(x + (double)(j - 2), B, C);
+ }
+}
+
+double CConvolutionKernel::LanczosWeight(double x, double radius)
+{
+ double ax = fabs(x);
+
+ if (ax == 0.0)
+ return 1.0;
+ else if (ax < radius)
+ return SINC(ax) * SINC(ax / radius);
+ else
+ return 0.0;
+}
+
+double CConvolutionKernel::BicubicWeight(double x, double B, double C)
+{
+ double ax = fabs(x);
+
+ if (ax<1.0)
+ {
+ return ((12 - 9*B - 6*C) * ax * ax * ax +
+ (-18 + 12*B + 6*C) * ax * ax +
+ (6 - 2*B))/6;
+ }
+ else if (ax<2.0)
+ {
+ return ((-B - 6*C) * ax * ax * ax +
+ (6*B + 30*C) * ax * ax + (-12*B - 48*C) *
+ ax + (8*B + 24*C)) / 6;
+ }
+ else
+ {
+ return 0.0;
+ }
+}
+
+
+//convert float to high byte/low byte, so the kernel can be loaded into an 8 bit texture
+//with height 2 and converted back to real float in the shader
+//it only works when the kernel texture uses nearest neighbour, but there's almost no difference
+//between that and linear interpolation
+void CConvolutionKernel::ToIntFract()
+{
+ m_intfractpixels = new uint8_t[m_size * 8];
+
+ for (int i = 0; i < m_size * 4; i++)
+ {
+ int value = MathUtils::round_int((m_floatpixels[i] + 1.0) / 2.0 * 65535.0);
+ if (value < 0)
+ value = 0;
+ else if (value > 65535)
+ value = 65535;
+
+ int integer = value / 256;
+ int fract = value % 256;
+
+ m_intfractpixels[i] = (uint8_t)integer;
+ m_intfractpixels[i + m_size * 4] = (uint8_t)fract;
+ }
+
+#if 0
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < m_size; j++)
+ {
+ printf("%i %f %f\n",
+ i * m_size + j,
+ ((double)m_intfractpixels[j * 4 + i] + (double)m_intfractpixels[j * 4 + i + m_size * 4] / 255.0) / 255.0 * 2.0 - 1.0,
+ m_floatpixels[j * 4 + i]);
+ }
+ }
+#endif
+}
+
--- xbmc/cores/VideoRenderers/VideoShaders/VideoFilterShader.h
+++ xbmc/cores/VideoRenderers/VideoShaders/VideoFilterShader.h
@@ -4,6 +4,7 @@
#ifdef HAS_GL
#include "../../../../guilib/Shader.h"
+#include "../../../settings/VideoSettings.h"
using namespace Shaders;
@@ -35,7 +36,7 @@
class BicubicFilterShader : public BaseVideoFilterShader
{
public:
- BicubicFilterShader(float B=0.0f, float C=0.0f);
+ BicubicFilterShader(float B=-1.0f, float C=-1.0f);
void OnCompiledAndLinked();
bool OnEnabled();
void Free();
@@ -55,6 +56,25 @@
float m_C;
};
+ class ConvolutionFilterShader : public BaseVideoFilterShader
+ {
+ public:
+ ConvolutionFilterShader(ESCALINGMETHOD method);
+ void OnCompiledAndLinked();
+ bool OnEnabled();
+ void Free();
+
+ protected:
+ // kernel textures
+ GLuint m_kernelTex1;
+
+ // shader handles to kernel textures
+ GLint m_hKernTex;
+
+ ESCALINGMETHOD m_method;
+ bool m_floattex; //if float textures are supported
+ };
+
} // end namespace
#endif
--- xbmc/cores/VideoRenderers/VideoShaders/ConvolutionKernels.h
+++ xbmc/cores/VideoRenderers/VideoShaders/ConvolutionKernels.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2005-2008 Team XBMC
+ * http://www.xbmc.org
+ *
+ * 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, 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef CONVOLUTIONKERNELS
+#define CONVOLUTIONKERNELS
+
+#include "system.h"
+#include "../../../settings/VideoSettings.h"
+
+class CConvolutionKernel
+{
+ public:
+ CConvolutionKernel(ESCALINGMETHOD method, int size);
+ ~CConvolutionKernel();
+
+ int GetSize() { return m_size; }
+ float* GetFloatPixels() { return m_floatpixels; }
+ uint8_t* GetIntFractPixels() { return m_intfractpixels; }
+
+ private:
+
+ void Lanczos2();
+ void Lanczos3Fast();
+ void Lanczos3();
+ void Bicubic(double B, double C);
+
+ double LanczosWeight(double x, double radius);
+ double BicubicWeight(double x, double B, double C);
+
+ void ToIntFract();
+
+ int m_size;
+ float* m_floatpixels;
+ uint8_t* m_intfractpixels;
+};
+
+#endif //CONVOLUTIONKERNELS
--- xbmc/cores/VideoRenderers/VideoShaders/Makefile
+++ xbmc/cores/VideoRenderers/VideoShaders/Makefile
@@ -1,5 +1,5 @@
INCLUDES=-I. -I.. -I../../ -I../../../ -I../../../linux -I../../../../guilib
-SRCS=YUV2RGBShader.cpp VideoFilterShader.cpp
+SRCS=YUV2RGBShader.cpp VideoFilterShader.cpp ConvolutionKernels.cpp
LIB=VideoShaders.a
--- xbmc/cores/VideoRenderers/LinuxRendererGL.cpp
+++ xbmc/cores/VideoRenderers/LinuxRendererGL.cpp
@@ -886,6 +886,19 @@
VerifyGLState();
+ if (m_scalingMethod == VS_SCALINGMETHOD_AUTO)
+ {
+ bool scaleSD = (int)m_sourceWidth < m_upscalingWidth && (int)m_sourceHeight < m_upscalingHeight &&
+ m_sourceHeight < 720 && m_sourceWidth < 1280;
+
+ if (Supports(VS_SCALINGMETHOD_VDPAU_HARDWARE))
+ m_scalingMethod = VS_SCALINGMETHOD_VDPAU_HARDWARE;
+ else if (Supports(VS_SCALINGMETHOD_LANCZOS3_FAST) && scaleSD)
+ m_scalingMethod = VS_SCALINGMETHOD_LANCZOS3_FAST;
+ else
+ m_scalingMethod = VS_SCALINGMETHOD_LINEAR;
+ }
+
switch (m_scalingMethod)
{
case VS_SCALINGMETHOD_NEAREST:
@@ -898,13 +911,10 @@
m_renderQuality = RQ_SINGLEPASS;
return;
+ case VS_SCALINGMETHOD_LANCZOS2:
+ case VS_SCALINGMETHOD_LANCZOS3_FAST:
+ case VS_SCALINGMETHOD_LANCZOS3:
case VS_SCALINGMETHOD_CUBIC:
- if(!glewIsSupported("GL_ARB_texture_float"))
- {
- CLog::Log(LOGERROR, "GL: hardware doesn't support GL_ARB_texture_float");
- break;
- }
-
if (!m_fbo.Initialize())
{
CLog::Log(LOGERROR, "GL: Error initializing FBO");
@@ -917,7 +927,7 @@
break;
}
- m_pVideoFilterShader = new BicubicFilterShader(0.3f, 0.3f);
+ m_pVideoFilterShader = new ConvolutionFilterShader(m_scalingMethod);
if (!m_pVideoFilterShader->CompileAndLink())
{
CLog::Log(LOGERROR, "GL: Error compiling and linking video filter shader");
@@ -928,8 +938,6 @@
m_renderQuality = RQ_MULTIPASS;
return;
- case VS_SCALINGMETHOD_LANCZOS2:
- case VS_SCALINGMETHOD_LANCZOS3:
case VS_SCALINGMETHOD_SINC8:
case VS_SCALINGMETHOD_NEDI:
CLog::Log(LOGERROR, "GL: TODO: This scaler has not yet been implemented");
@@ -1895,16 +1903,19 @@
bool CLinuxRendererGL::Supports(ESCALINGMETHOD method)
{
if(method == VS_SCALINGMETHOD_NEAREST
- || method == VS_SCALINGMETHOD_LINEAR)
+ || method == VS_SCALINGMETHOD_LINEAR
+ || method == VS_SCALINGMETHOD_AUTO)
return true;
-
- if(method == VS_SCALINGMETHOD_CUBIC
- && glewIsSupported("GL_ARB_texture_float")
- && glewIsSupported("GL_EXT_framebuffer_object")
- && m_renderMethod == RENDER_GLSL)
- return true;
-
+ if(method == VS_SCALINGMETHOD_CUBIC
+ || method == VS_SCALINGMETHOD_LANCZOS2
+ || method == VS_SCALINGMETHOD_LANCZOS3_FAST
+ || method == VS_SCALINGMETHOD_LANCZOS3)
+ {
+ if (glewIsSupported("GL_EXT_framebuffer_object") && (m_renderMethod & RENDER_GLSL))
+ return true;
+ }
+
if (g_advancedSettings.m_videoHighQualityScaling != SOFTWARE_UPSCALING_DISABLED)
{
if(method == VS_SCALINGMETHOD_BICUBIC_SOFTWARE
--- xbmc/GUIDialogVideoSettings.cpp
+++ xbmc/GUIDialogVideoSettings.cpp
@@ -103,6 +103,7 @@
entries.push_back(make_pair(VS_SCALINGMETHOD_LINEAR , 16302));
entries.push_back(make_pair(VS_SCALINGMETHOD_CUBIC , 16303));
entries.push_back(make_pair(VS_SCALINGMETHOD_LANCZOS2 , 16304));
+ entries.push_back(make_pair(VS_SCALINGMETHOD_LANCZOS3_FAST , 16315));
entries.push_back(make_pair(VS_SCALINGMETHOD_LANCZOS3 , 16305));
entries.push_back(make_pair(VS_SCALINGMETHOD_SINC8 , 16306));
// entries.push_back(make_pair(VS_SCALINGMETHOD_NEDI , ?????));
@@ -110,6 +111,7 @@
entries.push_back(make_pair(VS_SCALINGMETHOD_LANCZOS_SOFTWARE , 16308));
entries.push_back(make_pair(VS_SCALINGMETHOD_SINC_SOFTWARE , 16309));
entries.push_back(make_pair(VS_SCALINGMETHOD_VDPAU_HARDWARE , 13120));
+ entries.push_back(make_pair(VS_SCALINGMETHOD_AUTO , 16316));
/* remove unsupported methods */
for(vector >::iterator it = entries.begin(); it != entries.end();)
--- xbmc/Settings.cpp
+++ xbmc/Settings.cpp
@@ -772,7 +772,7 @@
GetInteger(pElement, "interlacemethod", interlaceMethod, VS_INTERLACEMETHOD_NONE, VS_INTERLACEMETHOD_NONE, VS_INTERLACEMETHOD_INVERSE_TELECINE);
m_stSettings.m_defaultVideoSettings.m_InterlaceMethod = (EINTERLACEMETHOD)interlaceMethod;
int scalingMethod;
- GetInteger(pElement, "scalingmethod", scalingMethod, VS_SCALINGMETHOD_LINEAR, VS_SCALINGMETHOD_NEAREST, VS_SCALINGMETHOD_CUBIC);
+ GetInteger(pElement, "scalingmethod", scalingMethod, VS_SCALINGMETHOD_LINEAR, VS_SCALINGMETHOD_NEAREST, VS_SCALINGMETHOD_AUTO);
m_stSettings.m_defaultVideoSettings.m_ScalingMethod = (ESCALINGMETHOD)scalingMethod;
GetInteger(pElement, "viewmode", m_stSettings.m_defaultVideoSettings.m_ViewMode, VIEW_MODE_NORMAL, VIEW_MODE_NORMAL, VIEW_MODE_CUSTOM);