1// OmniScale-Legacy
2// by Lior Halphon
3// ported to RetroArch's glsl format by hunterk
4
5/*
6MIT License
7
8Copyright (c) 2015-2016 Lior Halphon
9
10Permission is hereby granted, free of charge, to any person obtaining a copy
11of this software and associated documentation files (the "Software"), to deal
12in the Software without restriction, including without limitation the rights
13to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14copies of the Software, and to permit persons to whom the Software is
15furnished to do so, subject to the following conditions:
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26SOFTWARE.
27*/
28
29#if defined(VERTEX)
30
31#if __VERSION__ >= 130
32#define COMPAT_VARYING out
33#define COMPAT_ATTRIBUTE in
34#define COMPAT_TEXTURE texture
35#else
36#define COMPAT_VARYING varying
37#define COMPAT_ATTRIBUTE attribute
38#define COMPAT_TEXTURE texture2D
39#endif
40
41#ifdef GL_ES
42#define COMPAT_PRECISION mediump
43#else
44#define COMPAT_PRECISION
45#endif
46
47COMPAT_ATTRIBUTE vec4 VertexCoord;
48COMPAT_ATTRIBUTE vec4 COLOR;
49COMPAT_ATTRIBUTE vec4 TexCoord;
50COMPAT_VARYING vec4 COL0;
51COMPAT_VARYING vec4 TEX0;
52
53uniform mat4 MVPMatrix;
54uniform COMPAT_PRECISION int FrameDirection;
55uniform COMPAT_PRECISION int FrameCount;
56uniform COMPAT_PRECISION vec2 OutputSize;
57uniform COMPAT_PRECISION vec2 TextureSize;
58uniform COMPAT_PRECISION vec2 InputSize;
59
60// vertex compatibility #defines
61#define vTexCoord TEX0.xy
62#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
63#define outsize vec4(OutputSize, 1.0 / OutputSize)
64
65void main()
66{
67    gl_Position = MVPMatrix * VertexCoord;
68    COL0 = COLOR;
69    TEX0.xy = TexCoord.xy;
70}
71
72#elif defined(FRAGMENT)
73
74#if __VERSION__ >= 130
75#define COMPAT_VARYING in
76#define COMPAT_TEXTURE texture
77out vec4 FragColor;
78#else
79#define COMPAT_VARYING varying
80#define FragColor gl_FragColor
81#define COMPAT_TEXTURE texture2D
82#endif
83
84#ifdef GL_ES
85#ifdef GL_FRAGMENT_PRECISION_HIGH
86precision highp float;
87#else
88precision mediump float;
89precision mediump int;
90#endif
91#define COMPAT_PRECISION mediump
92#else
93#define COMPAT_PRECISION
94#endif
95
96uniform COMPAT_PRECISION int FrameDirection;
97uniform COMPAT_PRECISION int FrameCount;
98uniform COMPAT_PRECISION vec2 OutputSize;
99uniform COMPAT_PRECISION vec2 TextureSize;
100uniform COMPAT_PRECISION vec2 InputSize;
101uniform sampler2D Texture;
102COMPAT_VARYING vec4 TEX0;
103
104// fragment compatibility #defines
105#define Source Texture
106#define vTexCoord TEX0.xy
107
108#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
109#define outsize vec4(OutputSize, 1.0 / OutputSize)
110
111float quickDistance(vec4 a, vec4 b)
112{
113    return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z);
114}
115
116#define uResolution outsize.xy
117#define textureDimensions SourceSize.xy
118
119vec4 omniScale(sampler2D image, vec2 texCoord)
120{
121    vec2 pixel = texCoord * textureDimensions - vec2(0.5, 0.5);
122
123    vec4 q11 = COMPAT_TEXTURE(image, vec2(floor(pixel.x) / textureDimensions.x, floor(pixel.y) / textureDimensions.y+0.001));
124    vec4 q12 = COMPAT_TEXTURE(image, vec2(floor(pixel.x) / textureDimensions.x, ceil(pixel.y) / textureDimensions.y+0.001));
125    vec4 q21 = COMPAT_TEXTURE(image, vec2(ceil(pixel.x) / textureDimensions.x, floor(pixel.y) / textureDimensions.y+0.001));
126    vec4 q22 = COMPAT_TEXTURE(image, vec2(ceil(pixel.x) / textureDimensions.x, ceil(pixel.y) / textureDimensions.y+0.001));
127
128    vec2 pos = fract(pixel);
129
130    /* Special handling for diaonals */
131    bool hasDownDiagonal = false;
132    bool hasUpDiagonal = false;
133    if (q12 == q21 && q11 != q22) hasUpDiagonal = true;
134    else if (q12 != q21 && q11 == q22) hasDownDiagonal = true;
135    else if (q12 == q21 && q11 == q22) {
136        if (q11 == q12) return q11;
137        int diagonalBias = 0;
138		#ifdef GL_ES // unroll the loops
139			vec4 color;
140			// y = -1.0
141			// x = -1.0
142			color = COMPAT_TEXTURE(image, (pixel + vec2(-1.0, -1.0)) / textureDimensions);
143			if (color == q11) diagonalBias = diagonalBias + 1;
144			if (color == q12) diagonalBias = diagonalBias - 1;
145
146			// y = 0.0
147			// x = -1.0
148			color = COMPAT_TEXTURE(image, (pixel + vec2(-1.0, 0.0)) / textureDimensions);
149			if (color == q11) diagonalBias = diagonalBias + 1;
150			if (color == q12) diagonalBias = diagonalBias - 1;
151
152			// y = 1.0
153			// x = -1.0
154			color = COMPAT_TEXTURE(image, (pixel + vec2(-1.0, 1.0)) / textureDimensions);
155			if (color == q11) diagonalBias = diagonalBias + 1;
156			if (color == q12) diagonalBias = diagonalBias - 1;
157
158			// y = 2.0
159			// x = -1.0
160			color = COMPAT_TEXTURE(image, (pixel + vec2(-1.0, 2.0)) / textureDimensions);
161			if (color == q11) diagonalBias = diagonalBias + 1;
162			if (color == q12) diagonalBias = diagonalBias - 1;
163
164			// y = -1.0
165			// x = 0.0
166			color = COMPAT_TEXTURE(image, (pixel + vec2(0.0, -1.0)) / textureDimensions);
167			if (color == q11) diagonalBias = diagonalBias + 1;
168			if (color == q12) diagonalBias = diagonalBias - 1;
169
170			// y = 0.0
171			// x = 0.0
172			color = COMPAT_TEXTURE(image, (pixel + vec2(0.0, 0.0)) / textureDimensions);
173			if (color == q11) diagonalBias = diagonalBias + 1;
174			if (color == q12) diagonalBias = diagonalBias - 1;
175
176			// y = 1.0
177			// x = 0.0
178			color = COMPAT_TEXTURE(image, (pixel + vec2(0.0, 1.0)) / textureDimensions);
179			if (color == q11) diagonalBias = diagonalBias + 1;
180			if (color == q12) diagonalBias = diagonalBias - 1;
181
182			// y = 2.0
183			// x = 0.0
184			color = COMPAT_TEXTURE(image, (pixel + vec2(0.0, 2.0)) / textureDimensions);
185			if (color == q11) diagonalBias = diagonalBias + 1;
186			if (color == q12) diagonalBias = diagonalBias - 1;
187
188			// y = -1.0
189			// x = 1.0
190			color = COMPAT_TEXTURE(image, (pixel + vec2(1.0, -1.0)) / textureDimensions);
191			if (color == q11) diagonalBias = diagonalBias + 1;
192			if (color == q12) diagonalBias = diagonalBias - 1;
193
194			// y = 0.0
195			// x = 1.0
196			color = COMPAT_TEXTURE(image, (pixel + vec2(1.0, 0.0)) / textureDimensions);
197			if (color == q11) diagonalBias = diagonalBias + 1;
198			if (color == q12) diagonalBias = diagonalBias - 1;
199
200			// y = 1.0
201			// x = 1.0
202			color = COMPAT_TEXTURE(image, (pixel + vec2(1.0, 1.0)) / textureDimensions);
203			if (color == q11) diagonalBias = diagonalBias + 1;
204			if (color == q12) diagonalBias = diagonalBias - 1;
205
206			// y = 2.0
207			// x = 1.0
208			color = COMPAT_TEXTURE(image, (pixel + vec2(1.0, 2.0)) / textureDimensions);
209			if (color == q11) diagonalBias = diagonalBias + 1;
210			if (color == q12) diagonalBias = diagonalBias - 1;
211
212			// y = -1.0
213			// x = 2.0
214			color = COMPAT_TEXTURE(image, (pixel + vec2(0.0, -1.0)) / textureDimensions);
215			if (color == q11) diagonalBias = diagonalBias + 1;
216			if (color == q12) diagonalBias = diagonalBias - 1;
217
218			// y = 0.0
219			// x = 2.0
220			color = COMPAT_TEXTURE(image, (pixel + vec2(0.0, 0.0)) / textureDimensions);
221			if (color == q11) diagonalBias = diagonalBias + 1;
222			if (color == q12) diagonalBias = diagonalBias - 1;
223
224			// y = 1.0
225			// x = 2.0
226			color = COMPAT_TEXTURE(image, (pixel + vec2(0.0, 1.0)) / textureDimensions);
227			if (color == q11) diagonalBias = diagonalBias + 1;
228			if (color == q12) diagonalBias = diagonalBias - 1;
229
230			// y = 2.0
231			// x = 2.0
232			color = COMPAT_TEXTURE(image, (pixel + vec2(0.0, 2.0)) / textureDimensions);
233			if (color == q11) diagonalBias = diagonalBias + 1;
234			if (color == q12) diagonalBias = diagonalBias - 1;
235		#else
236		for (float y = -1.0; y < 3.0; y++) {
237            for (float x = -1.0; x < 3.0; x++) {
238                vec4 color = COMPAT_TEXTURE(image, (pixel + vec2(x, y)) / textureDimensions);
239                if (color == q11) diagonalBias++;
240                if (color == q12) diagonalBias--;
241            }
242        }
243		#endif
244        if (diagonalBias <= 0) {
245            hasDownDiagonal = true;
246        }
247        if (diagonalBias >= 0) {
248            hasUpDiagonal = true;
249        }
250    }
251
252    if (hasUpDiagonal || hasDownDiagonal) {
253        vec4 downDiagonalResult, upDiagonalResult;
254
255        if (hasUpDiagonal) {
256            float diagonalPos = pos.x + pos.y;
257
258            if (diagonalPos < 0.5) {
259                upDiagonalResult = q11;
260            }
261            else if (diagonalPos > 1.5) {
262                upDiagonalResult = q22;
263            }
264            else {
265                upDiagonalResult = q12;
266            }
267        }
268
269        if (hasDownDiagonal) {
270            float diagonalPos = 1.0 - pos.x + pos.y;
271
272            if (diagonalPos < 0.5) {
273                downDiagonalResult = q21;
274            }
275            else if (diagonalPos > 1.5) {
276                downDiagonalResult = q12;
277            }
278            else {
279                downDiagonalResult = q11;
280            }
281        }
282
283        if (!hasUpDiagonal) return downDiagonalResult;
284        if (!hasDownDiagonal) return upDiagonalResult;
285        return mix(downDiagonalResult, upDiagonalResult, 0.5);
286    }
287
288    vec4 r1 = mix(q11, q21, fract(pos.x));
289    vec4 r2 = mix(q12, q22, fract(pos.x));
290
291    vec4 unquantized = mix(r1, r2, fract(pos.y));
292
293    float q11d = quickDistance(unquantized, q11);
294    float q21d = quickDistance(unquantized, q21);
295    float q12d = quickDistance(unquantized, q12);
296    float q22d = quickDistance(unquantized, q22);
297
298    float best = min(q11d,
299                     min(q21d,
300                         min(q12d,
301                             q22d)));
302
303    if (q11d == best) {
304        return q11;
305    }
306
307    if (q21d == best) {
308        return q21;
309    }
310
311    if (q12d == best) {
312        return q12;
313    }
314
315    return q22;
316}
317
318vec4 scale(sampler2D tex, vec2 coord){
319    vec2 texCoord = coord;
320    vec2 pixel = vec2(1.0, 1.0) / uResolution;
321    // 4-pixel super sampling
322
323    vec4 q11 = omniScale(tex, texCoord + pixel * vec2(-0.25, -0.25));
324    vec4 q21 = omniScale(tex, texCoord + pixel * vec2(+0.25, -0.25));
325    vec4 q12 = omniScale(tex, texCoord + pixel * vec2(-0.25, +0.25));
326    vec4 q22 = omniScale(tex, texCoord + pixel * vec2(+0.25, +0.25));
327
328	return (q11 + q21 + q12 + q22) / 4.0;
329}
330
331void main()
332{
333    FragColor = scale(Source, vTexCoord);
334}
335#endif
336