[vert.glsl] #version 120 #if defined GL_ES precision highp float; #endif uniform mat4 u_ZoomSettings; uniform vec4 u_TexelSize; uniform vec4 u_ScreenSize; attribute vec2 a_TexCoord; attribute vec2 a_Vertex; varying vec4 v_CenterX, v_CenterY, v_IndexX, v_IndexY; void main(void) { gl_Position = vec4(a_Vertex, 0.0, 1.0); /* Center point in [-.5,.5], apply zoom and translation * transformation, and go back to texture coordinates * in [0,1]. That's the ideal point we would like to * compute the value for. Then add or remove half the * size of a texel: the distance from this new point to * the final point will be our error. */ vec4 offsets = vec4(0.5, -0.5, 0.015625, -0.015625); vec4 zoomscale = vec4(u_ZoomSettings[0][2], u_ZoomSettings[1][2], u_ZoomSettings[2][2], u_ZoomSettings[3][2]); vec4 zoomtx = vec4(u_ZoomSettings[0][0], u_ZoomSettings[1][0], u_ZoomSettings[2][0], u_ZoomSettings[3][0]); vec4 zoomty = vec4(u_ZoomSettings[0][1], u_ZoomSettings[1][1], u_ZoomSettings[2][1], u_ZoomSettings[3][1]); v_CenterX = zoomscale * a_TexCoord.x + zoomtx + offsets.xyxy * u_TexelSize.x; v_CenterY = zoomscale * a_TexCoord.y - zoomty + offsets.xyyx * u_TexelSize.y; /* Precompute the multiple of one texel where our ideal * point lies. The fragment shader will call floor() on * this value. We add or remove a slight offset to avoid * rounding issues at the image's edges. */ v_IndexX = v_CenterX * u_ScreenSize.z - offsets.zwzw; v_IndexY = v_CenterY * u_ScreenSize.w - offsets.zwwz; } [frag.glsl] #version 120 #if defined GL_ES precision highp float; #endif uniform vec4 u_TexelSize; uniform sampler2D u_Texture; varying vec4 v_CenterX, v_CenterY, v_IndexX, v_IndexY; void main(void) { vec4 v05 = vec4(0.5, 0.5, 0.5, 0.5); vec4 rx, ry, t0, dx, dy, dd; /* Get a pixel coordinate from each slice into rx & ry */ rx = u_TexelSize.x + u_TexelSize.z * floor(v_IndexX); ry = u_TexelSize.y + u_TexelSize.w * floor(v_IndexY); /* Compute inverse distance to expected pixel in dd, * and put zero if we fall outside the texture. */ t0 = step(abs(rx - v05), v05) * step(abs(ry - v05), v05); dx = rx - v_CenterX; dy = ry - v_CenterY; #if 0 vec4 dd = t0 * (abs(dx) + abs(dy)); vec4 dd = t0 / (0.001 + sqrt((dx * dx) + (dy * dy))); #endif dd = t0 / (0.000001 + (dx * dx) + (dy * dy)); /* Modify Y coordinate to select proper quarter. */ ry = ry * 0.25 + vec4(0.0, 0.25, 0.5, 0.75); #if 1 # if 0 /* XXX: disabled until we can autodetect i915 */ /* t1.x <-- dd.x > dd.y */ /* t1.y <-- dd.z > dd.w */ vec2 t1 = step(dd.xz, dd.yw); /* ret.x <-- max(rx.x, rx.y) wrt. t1.x */ /* ret.y <-- max(rx.z, rx.w) wrt. t1.y */ /* ret.z <-- max(ry.x, ry.y) wrt. t1.x */ /* ret.w <-- max(ry.z, ry.w) wrt. t1.y */ vec4 ret = mix(vec4(rx.xz, ry.xz), vec4(rx.yw, ry.yw), t1.xyxy); /* dd.x <-- max(dd.x, dd.y) */ /* dd.z <-- max(dd.z, dd.w) */ dd.xy = mix(dd.xz, dd.yw, t1); /* t2 <-- dd.x > dd.z */ float t2 = step(dd.x, dd.y); /* ret.x <-- max(ret.x, ret.y); */ /* ret.y <-- max(ret.z, ret.w); */ ret.xy = mix(ret.xz, ret.yw, t2); # else /* Fallback for i915 cards -- the trick to reduce the * number of operations is to compute both step(a,b) * and step(b,a) and hope that their sum is 1. This is * almost always the case, and when it isn't we can * afford to have a few wrong pixels. However, a real * problem is when panning the image, because half the * screen is likely to flicker. To avoid this problem, * we cheat a little (see m_translate comment above). */ vec4 t1 = step(dd.xzyw, dd.ywxz); vec4 ret = vec4(rx.xz, ry.xz) * t1.zwzw + vec4(rx.yw, ry.yw) * t1.xyxy; dd.xy = dd.xz * t1.zw + dd.yw * t1.xy; vec2 t2 = step(dd.xy, dd.yx); ret.xy = ret.xz * t2.yy + ret.yw * t2.xx; # endif /* Nearest neighbour */ gl_FragColor = texture2D(u_Texture, ret.xy); #else /* Alternate version: some kind of linear interpolation */ vec4 p0 = texture2D(u_Texture, vec2(rx.x, ry.x)); vec4 p1 = texture2D(u_Texture, vec2(rx.y, ry.y)); vec4 p2 = texture2D(u_Texture, vec2(rx.z, ry.z)); vec4 p3 = texture2D(u_Texture, vec2(rx.w, ry.w)); gl_FragColor = 1.0 / (dd.x + dd.y + dd.z + dd.w) * (dd.x * p0 + dd.y * p1 + dd.z * p2 + dd.w * p3); #endif } [vert.hlsl] void main(float2 a_Vertex : POSITION, float2 a_TexCoord : TEXCOORD0, uniform float4x4 u_ZoomSettings, uniform float4 u_TexelSize, uniform float4 u_ScreenSize, out float4 out_Position : POSITION0, out float4 v_CenterX : TEXCOORD0, out float4 v_CenterY : TEXCOORD1, out float4 v_IndexX : TEXCOORD2, out float4 v_IndexY : TEXCOORD3) { out_Position = float4(a_Vertex, 0.0, 1.0); float4 offsets = float4(0.5, -0.5, 0.015625, -0.015625); float4 zoomscale = float4(u_ZoomSettings[2][0], u_ZoomSettings[2][1], u_ZoomSettings[2][2], u_ZoomSettings[2][3]); float4 zoomtx = float4(u_ZoomSettings[0][0], u_ZoomSettings[0][1], u_ZoomSettings[0][2], u_ZoomSettings[0][3]); float4 zoomty = float4(u_ZoomSettings[1][0], u_ZoomSettings[1][1], u_ZoomSettings[1][2], u_ZoomSettings[1][3]); v_CenterX = zoomscale * a_TexCoord.x + zoomtx + offsets.xyxy * u_TexelSize.x; v_CenterY = zoomscale * a_TexCoord.y - zoomty + offsets.xyyx * u_TexelSize.y; v_IndexX = v_CenterX * u_ScreenSize.z - offsets.zwzw; v_IndexY = v_CenterY * u_ScreenSize.w - offsets.zwwz; } [frag.hlsl] void main(in float4 v_CenterX : TEXCOORD0, in float4 v_CenterY : TEXCOORD1, in float4 v_IndexX : TEXCOORD2, in float4 v_IndexY : TEXCOORD3, uniform float4 u_TexelSize, uniform sampler2D u_Texture, out float4 out_FragColor : COLOR) { float4 v05 = float4(0.5, 0.5, 0.5, 0.5); float4 rx, ry, t0, dx, dy, dd; rx = u_TexelSize.x + u_TexelSize.z * floor(v_IndexX); ry = u_TexelSize.y + u_TexelSize.w * floor(v_IndexY); t0 = step(abs(rx - v05), v05) * step(abs(ry - v05), v05); dx = rx - v_CenterX; dy = ry - v_CenterY; dd = t0 / (0.000001 + (dx * dx) + (dy * dy)); ry = ry * 0.25 + float4(0.0, 0.25, 0.5, 0.75); float2 t1 = step(dd.xz, dd.yw); float4 ret = lerp(float4(rx.xz, ry.xz), float4(rx.yw, ry.yw), t1.xyxy); dd.xy = lerp(dd.xz, dd.yw, t1); float t2 = step(dd.x, dd.y); ret.xy = lerp(ret.xz, ret.yw, t2); out_FragColor = tex2D(u_Texture, ret.xy); }