forked from LIJI32/SameBoy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introducing the OmniScale (beta) algorithm to SameBoy
- Loading branch information
Showing
12 changed files
with
287 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# Scaling | ||
|
||
Starting with version 0.4, the Cocoa version of SameBoy supports several GPU-accelerated scaling algorithms, some of which made their premiere at SameBoy. This document describes the algorithms supported by SameBoy. | ||
|
||
## General-purpose Scaling Algorithms | ||
Common algorithms that were not made specifically for pixel art | ||
|
||
### Nearest Neighbor | ||
A simple pixelated scaling algorithm we all know and love. This is the default filter. | ||
|
||
### Bilinear | ||
An algorithm that fills "missing" pixels using a bilinear interpolation, causing a blurry image | ||
|
||
### Smooth Bilinear | ||
A variant of bilinear filtering that applies a smooth curve to the bilinear interpolation. The results look similar to the algorithm Apple uses when scaling non-Retina graphics for Retina Displays. | ||
|
||
## The ScaleNx Family | ||
The ScaleNx family is a group of algorithm that scales pixel art by the specified factor using simple pattern-based rules. The Scale3x algorithm is not yet supported in SameBoy. | ||
|
||
### Scale2x | ||
The most simple algorithm of the family. It scales the image by a 2x factor without introducing new colors. | ||
|
||
### Scale4x | ||
This algorithm applies the Scale2x algorithm twice to scale the image by a 4x factor. | ||
|
||
### Anti-aliased Scale2x | ||
A variant of Scale2x exclusive to SameBoy that blends the Scale2x output with the Nearest Neighbor output. The specific traits of Scale2x makes this blend produce nicely looking anti-aliased output. | ||
|
||
### Anti-aliased Scale4x | ||
Another exclusive algorithm that works by applying the Anti-aliased Scale2x algorithm twice | ||
|
||
## The OmniScale Family (beta) | ||
OmniScale is an exclusive algorithm developed for SameBoy. It combines pattern-based rules with a unique locally paletted bilinear filtering technique to scale an image by any factor, including non-integer factors. The algorithm is currently in beta, and its pattern-based rule do not currently detect 30- and 60-degree diagonals, making them look jaggy. | ||
|
||
### OmniScale | ||
The base version of the algorithm, which generates aliased output with very few new colors introduced. | ||
|
||
### Anti-aliased OmniScale | ||
A variant of OmniScale that produces anti-aliased output using 2x super-sampling. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
|
||
float quickDistance(vec4 a, vec4 b) | ||
{ | ||
return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z); | ||
} | ||
|
||
vec4 omniScale(sampler2D image, vec2 texCoord) | ||
{ | ||
vec2 pixel = texCoord * textureDimensions - vec2(0.5, 0.5); | ||
|
||
vec4 q11 = texture2D(image, vec2(floor(pixel.x) / textureDimensions.x, floor(pixel.y) / textureDimensions.y)); | ||
vec4 q12 = texture2D(image, vec2(floor(pixel.x) / textureDimensions.x, ceil(pixel.y) / textureDimensions.y)); | ||
vec4 q21 = texture2D(image, vec2(ceil(pixel.x) / textureDimensions.x, floor(pixel.y) / textureDimensions.y)); | ||
vec4 q22 = texture2D(image, vec2(ceil(pixel.x) / textureDimensions.x, ceil(pixel.y) / textureDimensions.y)); | ||
|
||
vec2 pos = fract(pixel); | ||
|
||
/* Special handling for diaonals */ | ||
bool hasDownDiagonal = false; | ||
bool hasUpDiagonal = false; | ||
if (q12 == q21 && q11 != q22) hasUpDiagonal = true; | ||
else if (q12 != q21 && q11 == q22) hasDownDiagonal = true; | ||
else if (q12 == q21 && q11 == q22) { | ||
if (q11 == q12) return q11; | ||
int diagonalBias = 0; | ||
for (float y = -1.0; y < 3.0; y++) { | ||
for (float x = -1.0; x < 3.0; x++) { | ||
vec4 color = texture2D(image, (pixel + vec2(x, y)) / textureDimensions); | ||
if (color == q11) diagonalBias++; | ||
if (color == q12) diagonalBias--; | ||
} | ||
} | ||
if (diagonalBias <= 0) { | ||
hasDownDiagonal = true; | ||
} | ||
if (diagonalBias >= 0) { | ||
hasUpDiagonal = true; | ||
} | ||
} | ||
|
||
if (hasUpDiagonal || hasDownDiagonal) { | ||
vec4 downDiagonalResult, upDiagonalResult; | ||
|
||
if (hasUpDiagonal) { | ||
float diagonalPos = pos.x + pos.y; | ||
|
||
if (diagonalPos < 0.5) { | ||
upDiagonalResult = q11; | ||
} | ||
else if (diagonalPos > 1.5) { | ||
upDiagonalResult = q22; | ||
} | ||
else { | ||
upDiagonalResult = q12; | ||
} | ||
} | ||
|
||
if (hasDownDiagonal) { | ||
float diagonalPos = 1.0 - pos.x + pos.y; | ||
|
||
if (diagonalPos < 0.5) { | ||
downDiagonalResult = q21; | ||
} | ||
else if (diagonalPos > 1.5) { | ||
downDiagonalResult = q12; | ||
} | ||
else { | ||
downDiagonalResult = q11; | ||
} | ||
} | ||
|
||
if (!hasUpDiagonal) return downDiagonalResult; | ||
if (!hasDownDiagonal) return upDiagonalResult; | ||
return mix(downDiagonalResult, upDiagonalResult, 0.5); | ||
} | ||
|
||
vec4 r1 = mix(q11, q21, fract(pos.x)); | ||
vec4 r2 = mix(q12, q22, fract(pos.x)); | ||
|
||
vec4 unqunatized = mix(r1, r2, fract(pos.y)); | ||
|
||
float q11d = quickDistance(unqunatized, q11); | ||
float q21d = quickDistance(unqunatized, q21); | ||
float q12d = quickDistance(unqunatized, q12); | ||
float q22d = quickDistance(unqunatized, q22); | ||
|
||
float best = min(q11d, | ||
min(q21d, | ||
min(q12d, | ||
q22d))); | ||
|
||
if (q11d == best) { | ||
return q11; | ||
} | ||
|
||
if (q21d == best) { | ||
return q21; | ||
} | ||
|
||
if (q12d == best) { | ||
return q12; | ||
} | ||
|
||
return q22; | ||
} | ||
|
||
vec4 filter(sampler2D image) | ||
{ | ||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution; | ||
vec2 pixel = vec2(1.0, 1.0) / uResolution; | ||
// 4-pixel super sampling | ||
|
||
vec4 q11 = omniScale(image, texCoord + pixel * vec2(-0.25, -0.25)); | ||
vec4 q21 = omniScale(image, texCoord + pixel * vec2(+0.25, -0.25)); | ||
vec4 q12 = omniScale(image, texCoord + pixel * vec2(-0.25, +0.25)); | ||
vec4 q22 = omniScale(image, texCoord + pixel * vec2(+0.25, +0.25)); | ||
|
||
return (q11 + q21 + q12 + q22) / 4.0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
|
||
float quickDistance(vec4 a, vec4 b) | ||
{ | ||
return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z); | ||
} | ||
|
||
vec4 filter(sampler2D image) | ||
{ | ||
vec2 texCoord = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y) / uResolution; | ||
|
||
vec2 pixel = texCoord * textureDimensions - vec2(0.5, 0.5); | ||
|
||
vec4 q11 = texture2D(image, (pixel ) / textureDimensions); | ||
vec4 q12 = texture2D(image, (pixel + vec2(0.0, 1.0)) / textureDimensions); | ||
vec4 q21 = texture2D(image, (pixel + vec2(1.0, 0.0)) / textureDimensions); | ||
vec4 q22 = texture2D(image, (pixel + vec2(1.0, 1.0)) / textureDimensions); | ||
|
||
vec2 pos = fract(pixel); | ||
|
||
/* Special handling for diaonals */ | ||
bool hasDownDiagonal = false; | ||
bool hasUpDiagonal = false; | ||
if (q12 == q21 && q11 != q22) hasUpDiagonal = true; | ||
else if (q12 != q21 && q11 == q22) hasDownDiagonal = true; | ||
else if (q12 == q21 && q11 == q22) { | ||
if (q11 == q12) return q11; | ||
int diagonalBias = 0; | ||
for (float y = -1.0; y < 3.0; y++) { | ||
for (float x = -1.0; x < 3.0; x++) { | ||
vec4 color = texture2D(image, (pixel + vec2(x, y)) / textureDimensions); | ||
if (color == q11) diagonalBias++; | ||
if (color == q12) diagonalBias--; | ||
} | ||
} | ||
if (diagonalBias <= 0) { | ||
hasDownDiagonal = true; | ||
} | ||
if (diagonalBias >= 0) { | ||
hasUpDiagonal = true; | ||
} | ||
} | ||
|
||
if (hasUpDiagonal || hasDownDiagonal) { | ||
vec4 downDiagonalResult, upDiagonalResult; | ||
|
||
if (hasUpDiagonal) { | ||
float diagonalPos = pos.x + pos.y; | ||
|
||
if (diagonalPos < 0.5) { | ||
upDiagonalResult = q11; | ||
} | ||
else if (diagonalPos > 1.5) { | ||
upDiagonalResult = q22; | ||
} | ||
else { | ||
upDiagonalResult = q12; | ||
} | ||
} | ||
|
||
if (hasDownDiagonal) { | ||
float diagonalPos = 1.0 - pos.x + pos.y; | ||
|
||
if (diagonalPos < 0.5) { | ||
downDiagonalResult = q21; | ||
} | ||
else if (diagonalPos > 1.5) { | ||
downDiagonalResult = q12; | ||
} | ||
else { | ||
downDiagonalResult = q11; | ||
} | ||
} | ||
|
||
if (!hasUpDiagonal) return downDiagonalResult; | ||
if (!hasDownDiagonal) return upDiagonalResult; | ||
return mix(downDiagonalResult, upDiagonalResult, 0.5); | ||
} | ||
|
||
vec4 r1 = mix(q11, q21, fract(pos.x)); | ||
vec4 r2 = mix(q12, q22, fract(pos.x)); | ||
|
||
vec4 unqunatized = mix(r1, r2, fract(pos.y)); | ||
|
||
float q11d = quickDistance(unqunatized, q11); | ||
float q21d = quickDistance(unqunatized, q21); | ||
float q12d = quickDistance(unqunatized, q12); | ||
float q22d = quickDistance(unqunatized, q22); | ||
|
||
float best = min(q11d, | ||
min(q21d, | ||
min(q12d, | ||
q22d))); | ||
|
||
if (q11d == best) { | ||
return q11; | ||
} | ||
|
||
if (q21d == best) { | ||
return q21; | ||
} | ||
|
||
if (q12d == best) { | ||
return q12; | ||
} | ||
|
||
return q22; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters