From 293b05fec6cf6b5cbd8e2a689f7e82c40483c811 Mon Sep 17 00:00:00 2001
From: Filippo Scognamiglio <flscogna@gmail.com>
Date: Mon, 5 Nov 2018 00:16:52 +0100
Subject: [PATCH] Add subpixels rasterization and improve existing ones.

---
 app/qml/ApplicationSettings.qml |   3 +
 app/qml/SettingsScreenTab.qml   |   2 +-
 app/qml/ShaderTerminal.qml      |  30 +++-------
 app/qml/TerminalContainer.qml   | 100 +++++++++++++++++---------------
 4 files changed, 65 insertions(+), 70 deletions(-)

diff --git a/app/qml/ApplicationSettings.qml b/app/qml/ApplicationSettings.qml
index 37f330d..74a282a 100644
--- a/app/qml/ApplicationSettings.qml
+++ b/app/qml/ApplicationSettings.qml
@@ -89,6 +89,7 @@ QtObject{
     readonly property int no_rasterization: 0
     readonly property int scanline_rasterization: 1
     readonly property int pixel_rasterization: 2
+    readonly property int subpixel_rasterization: 3
 
     property int rasterization: no_rasterization
 
@@ -113,6 +114,8 @@ QtObject{
             State { when: rasterization == scanline_rasterization
                 PropertyChanges {target: fontManager; source: "FontScanlines.qml" } },
             State { when: rasterization == pixel_rasterization;
+                PropertyChanges {target: fontManager; source: "FontPixels.qml" } },
+            State { when: rasterization == subpixel_rasterization;
                 PropertyChanges {target: fontManager; source: "FontPixels.qml" } }
         ]
 
diff --git a/app/qml/SettingsScreenTab.qml b/app/qml/SettingsScreenTab.qml
index 5777e65..f8d234f 100644
--- a/app/qml/SettingsScreenTab.qml
+++ b/app/qml/SettingsScreenTab.qml
@@ -33,7 +33,7 @@ Tab{
                 id: rasterizationBox
                 property string selectedElement: model[currentIndex]
                 anchors.fill: parent
-                model: [qsTr("Default"), qsTr("Scanlines"), qsTr("Pixels")]
+                model: [qsTr("Default"), qsTr("Scanlines"), qsTr("Pixels"), qsTr("Subpixels")]
                 currentIndex: appSettings.rasterization
                 onCurrentIndexChanged: {
                     appSettings.rasterization = currentIndex
diff --git a/app/qml/ShaderTerminal.qml b/app/qml/ShaderTerminal.qml
index e0938a9..122a65f 100644
--- a/app/qml/ShaderTerminal.qml
+++ b/app/qml/ShaderTerminal.qml
@@ -27,6 +27,7 @@ ShaderEffect {
     property ShaderEffectSource source
     property ShaderEffectSource blurredSource
     property ShaderEffectSource bloomSource
+    property ShaderEffectSource rasterizationSource
 
     property color fontColor: appSettings.fontColor
     property color backgroundColor: appSettings.backgroundColor
@@ -58,12 +59,6 @@ ShaderEffect {
 
     property real screen_brightness: appSettings.brightness * 1.5 + 0.5
 
-    // This is the average value of the abs(sin) function. Needed to avoid aliasing.
-    readonly property real absSinAvg: 0.63661828335466886
-    property size rasterizationSmooth: Qt.size(
-                                           Utils.clamp(2.0 * virtual_resolution.width / (width * devicePixelRatio), 0.0, 1.0),
-                                           Utils.clamp(2.0 * virtual_resolution.height / (height * devicePixelRatio), 0.0, 1.0))
-
     property real dispX
     property real dispY
     property size virtual_resolution
@@ -165,10 +160,12 @@ ShaderEffect {
         uniform lowp float screen_brightness;
 
         uniform highp vec2 virtual_resolution;
-        uniform highp vec2 rasterizationSmooth;
         uniform highp float dispX;
         uniform highp float dispY;" +
 
+        (appSettings.rasterization != appSettings.no_rasterization ?
+            "uniform lowp sampler2D rasterizationSource;" : "") +
+
         (bloom !== 0 ? "
             uniform highp sampler2D bloomSource;
             uniform lowp float bloom;" : "") +
@@ -207,20 +204,7 @@ ShaderEffect {
                 return fract(smoothstep(-120.0, 0.0, coords.y - (virtual_resolution.y + 120.0) * fract(time * 0.00015)));
             }" : "") +
 
-        "highp float getScanlineIntensity(vec2 coords) {
-            highp float result = 1.0;" +
-
-           (appSettings.rasterization != appSettings.no_rasterization ?
-               "float val = abs(sin(coords.y * virtual_resolution.y * "+Math.PI+"));
-                result *= mix(val, " + absSinAvg + ", rasterizationSmooth.y);" : "") +
-           (appSettings.rasterization == appSettings.pixel_rasterization ?
-               "val = abs(sin(coords.x * virtual_resolution.x * "+Math.PI+"));
-                result *= mix(val, " + absSinAvg + ", rasterizationSmooth.x);" : "") + "
-
-           return result;
-        }
-
-        float min2(vec2 v) {
+        "float min2(vec2 v) {
             return min(v.x, v.y);
         }
 
@@ -310,7 +294,9 @@ ShaderEffect {
             :
                 "vec3 finalColor = mix(backgroundColor.rgb, fontColor.rgb, greyscale_color);") +
 
-            "finalColor *= getScanlineIntensity(coords);" +
+            (appSettings.rasterization != appSettings.no_rasterization ? "
+                finalColor *= texture2D(rasterizationSource, staticCoords * (virtual_resolution)).rgb;
+            " : "") +
 
             (bloom !== 0 ?
                 "vec4 bloomFullColor = texture2D(bloomSource, coords);
diff --git a/app/qml/TerminalContainer.qml b/app/qml/TerminalContainer.qml
index 90ee1f3..5fddd2a 100644
--- a/app/qml/TerminalContainer.qml
+++ b/app/qml/TerminalContainer.qml
@@ -69,64 +69,70 @@ ShaderTerminal{
 
     bloomSource: bloomSourceLoader.item
 
-    // This shader might be useful in the future. Since we used it only for a couple
-    // of calculations is probably best to move those in the main shader. If in the future
-    // we need to store another fullScreen channel this might be handy.
+    Loader {
+        id: rasterizationEffectLoader
+        active: appSettings.rasterization != appSettings.no_rasterization
+        asynchronous: true
+        sourceComponent: ShaderEffect {
+            id: rasterizationEffect
+            width: 16
+            height: 16
 
-//    ShaderEffect {
-//        id: rasterizationEffect
-//        width: parent.width
-//        height: parent.height
-//        property real outColor: 0.0
-//        property real dispX: (5 / width) * appSettings.windowScaling
-//        property real dispY: (5 / height) * appSettings.windowScaling
-//        property size virtual_resolution: terminal.virtualResolution
+            blending: false
 
-//        blending: false
+            fragmentShader:
+                "uniform lowp float qt_Opacity;" +
 
-//        fragmentShader:
-//            "uniform lowp float qt_Opacity;" +
+                "varying highp vec2 qt_TexCoord0;
 
-//            "varying highp vec2 qt_TexCoord0;
-//             uniform highp vec2 virtual_resolution;
-//             uniform highp float dispX;
-//             uniform highp float dispY;
-//             uniform mediump float outColor;
+                 highp float getScanlineIntensity(vec2 coords) {
+                     highp float result = 1.0;" +
 
-//             highp float getScanlineIntensity(vec2 coords) {
-//                 highp float result = 1.0;" +
+                    (appSettings.rasterization == appSettings.scanline_rasterization ?
+                        "result *= (smoothstep(0.0, 0.5, coords.y) - smoothstep(0.5, 1.0, coords.y));" : "") +
 
-//                (appSettings.rasterization != appSettings.no_rasterization ?
-//                    "result *= abs(sin(coords.y * virtual_resolution.y * "+Math.PI+"));" : "") +
-//                (appSettings.rasterization == appSettings.pixel_rasterization ?
-//                    "result *= abs(sin(coords.x * virtual_resolution.x * "+Math.PI+"));" : "") + "
+                    (appSettings.rasterization == appSettings.pixel_rasterization ?
+                       "result *= (smoothstep(0.0, 0.25, coords.y) - smoothstep(0.75, 1.0, coords.y));
+                        result *= (smoothstep(0.0, 0.25, coords.x) - smoothstep(0.75, 1.0, coords.x));" : "") +
 
-//                return result;
-//             }" +
+                    (appSettings.rasterization == appSettings.subpixel_rasterization ?
+                       "result *= (smoothstep(0.0, 0.25, coords.y) - smoothstep(0.75, 1.0, coords.y));" : "") + "
 
-//            "void main() {" +
-//                "highp float color = getScanlineIntensity(qt_TexCoord0);" +
+                    return result;
+                 }" +
 
-//                "float distance = length(vec2(0.5) - qt_TexCoord0);" +
-//                "color = mix(color, 0.0, 1.2 * distance * distance);" +
+                "void main() {" +
 
-//                "color *= outColor + smoothstep(0.00, dispX, qt_TexCoord0.x) * (1.0 - outColor);" +
-//                "color *= outColor + smoothstep(0.00, dispY, qt_TexCoord0.y) * (1.0 - outColor);" +
-//                "color *= outColor + (1.0 - smoothstep(1.00 - dispX, 1.00, qt_TexCoord0.x)) * (1.0 - outColor);" +
-//                "color *= outColor + (1.0 - smoothstep(1.00 - dispY, 1.00, qt_TexCoord0.y)) * (1.0 - outColor);" +
+                    (appSettings.rasterization == appSettings.subpixel_rasterization ?
+                        "highp vec3 color = vec3(0.0);
+                        color += vec3(1.0, 0.25, 0.25) * (step(0.0, qt_TexCoord0.x) - step(1.0/3.0, qt_TexCoord0.x));
+                        color += vec3(0.25, 1.0, 0.25) * (step(1.0/3.0, qt_TexCoord0.x) - step(2.0/3.0, qt_TexCoord0.x));
+                        color += vec3(0.25, 0.25, 1.0) * (step(2.0/3.0, qt_TexCoord0.x) - step(3.0/3.0, qt_TexCoord0.x));"
+                    :
+                        "highp vec3 color = vec3(1.0);" ) +
 
-//                "gl_FragColor.a = color;" +
-//            "}"
+                    "color *= getScanlineIntensity(qt_TexCoord0);
+                     gl_FragColor = vec4(color, 1.0);" +
+                "}"
 
-//        onStatusChanged: if (log) console.log(log) //Print warning messages
-//    }
+            onStatusChanged: if (log) console.log(log) //Print warning messages
+        }
+    }
 
-//    rasterizationSource: ShaderEffectSource{
-//        id: rasterizationEffectSource
-//        sourceItem: rasterizationEffect
-//        hideSource: true
-//        smooth: true
-//        wrapMode: ShaderEffectSource.ClampToEdge
-//        visible: false
-//    }
+    Loader {
+        id: rasterizationSourceLoader
+        active: appSettings.rasterization != appSettings.no_rasterization
+        asynchronous: true
+
+        sourceComponent: ShaderEffectSource {
+            sourceItem: rasterizationEffectLoader.item
+            hideSource: true
+            smooth: true
+            wrapMode: ShaderEffectSource.Repeat
+            visible: false
+            mipmap: true
+        }
+    }
+
+    rasterizationSource: rasterizationSourceLoader.item
 }