sai.frag 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /* MegaZeux
  2. *
  3. * Copyright (C) 2019 Alice Rowan <petrifiedrowan@gmail.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. */
  19. // This is adapted fairly closely from the 2xSaI C++ source code.
  20. // Specifically, this is based on the "Scale 2xSaI" mode designed for an
  21. // arbitrary destination size.
  22. //
  23. // 2xSaI is Copyright (c) 1999-2001 by Derek Liauw Kie Fa.
  24. // 2xSaI is free under GPL. I hope you'll give me appropriate credit.
  25. // If you want another license for your (free) project, contact me.
  26. //
  27. // The author confirmed in October 2009 that this means specifically GPL 2+.
  28. // https://www.redhat.com/archives/fedora-legal-list/2009-October/msg00025.html
  29. #version 110
  30. uniform sampler2D baseMap;
  31. varying vec2 vTexcoord;
  32. // The mix() wrapper used here utilizes a sigmoid function to weigh the
  33. // interpolation towards the closer source color. This is done to make this
  34. // scaling method look better with MegaZeux-style graphics.
  35. //
  36. // A higher MULTIPLIER value produces sharper edges, but too high of a value
  37. // will make things blocky. Comment out the SHARPEN_EDGES line to disable this
  38. // (making this shader blurry but fairly accurate to the original Scale 2xSaI).
  39. #define SHARPEN_EDGES 1
  40. #define MULTIPLIER 10.0
  41. #define TEX_SCREEN_WIDTH 1024.0
  42. #define TEX_SCREEN_HEIGHT 512.0
  43. #define PIXEL_X 1.0 / TEX_SCREEN_WIDTH
  44. #define PIXEL_Y 1.0 / TEX_SCREEN_HEIGHT
  45. #define HALF_PIXEL_X 0.5 / TEX_SCREEN_WIDTH
  46. #define HALF_PIXEL_Y 0.5 / TEX_SCREEN_HEIGHT
  47. vec4 _mix(vec4 A, vec4 B, float x)
  48. {
  49. #ifdef SHARPEN_EDGES
  50. x = (x - 0.5) * MULTIPLIER;
  51. x = 1.0 / (exp(-x) + 1.0);
  52. #endif
  53. return mix(A, B, x);
  54. }
  55. void main(void)
  56. {
  57. /**
  58. * E F
  59. * G A B I
  60. * H C D J
  61. * K L
  62. */
  63. vec4 A, B, C, D, E, F, G, H, I, J, K, L;
  64. float x = floor(vTexcoord.x * TEX_SCREEN_WIDTH) / TEX_SCREEN_WIDTH + HALF_PIXEL_X;
  65. float y = floor(vTexcoord.y * TEX_SCREEN_HEIGHT) / TEX_SCREEN_HEIGHT + HALF_PIXEL_Y;
  66. float x_fr = fract(vTexcoord.x * TEX_SCREEN_WIDTH);
  67. float y_fr = fract(vTexcoord.y * TEX_SCREEN_HEIGHT);
  68. float x_fr2;
  69. float y_fr2;
  70. float f1;
  71. float f2;
  72. vec4 res;
  73. A = texture2D(baseMap, vec2(x, y));
  74. B = texture2D(baseMap, vec2(x + PIXEL_X, y));
  75. C = texture2D(baseMap, vec2(x, y + PIXEL_Y));
  76. D = texture2D(baseMap, vec2(x + PIXEL_X, y + PIXEL_Y));
  77. if(A == B && C == D && A == C)
  78. {
  79. res = A;
  80. }
  81. else
  82. if(A == D && B != C)
  83. {
  84. E = texture2D(baseMap, vec2(x, y - PIXEL_Y));
  85. G = texture2D(baseMap, vec2(x - PIXEL_X, y));
  86. L = texture2D(baseMap, vec2(x + PIXEL_X, y + PIXEL_Y * 2.0));
  87. J = texture2D(baseMap, vec2(x + PIXEL_X * 2.0, y + PIXEL_Y));
  88. f1 = x_fr / 2.0 + 0.25;
  89. f2 = y_fr / 2.0 + 0.25;
  90. if(y_fr <= f1 && A == J && A != E) // Close to B
  91. res = _mix(A, B, f1 - y_fr);
  92. else
  93. if(y_fr >= f1 && A == G && A != L) // Close to C
  94. res = _mix(A, C, y_fr - f1);
  95. else
  96. if(x_fr >= f2 && A == E && A != J) // Close to B
  97. res = _mix(A, B, x_fr - f2);
  98. else
  99. if(x_fr <= f2 && A == L && A != G) // Close to C
  100. res = _mix(A, C, f2 - x_fr);
  101. else
  102. if(y_fr >= x_fr) // Close to C
  103. res = _mix(A, C, y_fr - x_fr);
  104. else
  105. //if(y_fr <= x_fr) // Close to B
  106. res = _mix(A, B, x_fr - y_fr);
  107. }
  108. else
  109. if(B == C && A != D)
  110. {
  111. F = texture2D(baseMap, vec2(x + PIXEL_X, y - PIXEL_Y));
  112. H = texture2D(baseMap, vec2(x - PIXEL_X, y + PIXEL_Y));
  113. I = texture2D(baseMap, vec2(x + PIXEL_X * 2.0, y));
  114. K = texture2D(baseMap, vec2(x, y + PIXEL_Y * 2.0));
  115. f1 = x_fr / 2.0 + 0.25;
  116. f2 = y_fr / 2.0 + 0.25;
  117. x_fr2 = 1.0 - x_fr;
  118. y_fr2 = 1.0 - y_fr;
  119. if(y_fr2 >= f1 && B == H && B != F) // Close to A
  120. res = _mix(B, A, y_fr2 - f1);
  121. else
  122. if(y_fr2 <= f1 && B == I && B != K) // Close to D
  123. res = _mix(B, D, f1 - y_fr2);
  124. else
  125. if(x_fr2 >= f2 && B == F && B != H) // Close to A
  126. res = _mix(B, A, x_fr2 - f2);
  127. else
  128. if(x_fr2 <= f2 && B == K && B != I) // Close to D
  129. res = _mix(B, D, f2 - x_fr2);
  130. else
  131. if(y_fr2 >= x_fr) // Close to A
  132. res = _mix(B, A, y_fr2 - x_fr);
  133. else
  134. //if(y_fr2 <= x_fr) // Close to D
  135. res = _mix(B, D, x_fr - y_fr2);
  136. }
  137. else
  138. {
  139. res = _mix(_mix(A, B, x_fr), _mix(C, D, x_fr), y_fr);
  140. }
  141. gl_FragColor = res;
  142. }