Last active
October 18, 2022 01:24
-
-
Save stanio/a0483c34cfe97ae9483bfd553a1a794d to your computer and use it in GitHub Desktop.
Super-xBR Scaler: https://pastebin.com/cbH8ZQQT (Java version)
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
/* | |
* Copyright (c) 2016 Hyllian - [email protected] | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
* THE SOFTWARE. | |
*/ | |
//package ; | |
/** | |
* Super-xBR Scaler | |
* | |
* @see https://pastebin.com/cbH8ZQQT | |
*/ | |
public class SuperXBR { | |
private static int R(int _col) { return (_col >> 0) & 0xFF; } | |
private static int G(int _col) { return (_col >> 8) & 0xFF; } | |
private static int B(int _col) { return (_col >> 16) & 0xFF; } | |
private static int A(int _col) { return (_col >> 24) & 0xFF; } | |
private static final float wgt1 = 0.129633f; | |
private static final float wgt2 = 0.175068f; | |
private static final float w1 = -wgt1; | |
private static final float w2 = wgt1 + 0.5f; | |
private static final float w3 = -wgt2; | |
private static final float w4 = wgt2 + 0.5f; | |
private static float df(float A, float B) | |
{ | |
return Math.abs(A - B); | |
} | |
private static float min4(float a, float b, float c, float d) | |
{ | |
return Math.min(Math.min(a, b), Math.min(c, d)); | |
} | |
private static float max4(float a, float b, float c, float d) | |
{ | |
return Math.max(Math.max(a, b), Math.max(c, d)); | |
} | |
private static float clamp(float x, float floor, float ceil) | |
{ | |
return Math.max(Math.min(x, ceil), floor); | |
} | |
private static int clamp(int x, int floor, int ceil) | |
{ | |
return Math.max(Math.min(x, ceil), floor); | |
} | |
/* | |
* P1 | |
* |P0|B |C |P1| C F4 |a0|b1|c2|d3| | |
* |D |E |F |F4| B F I4 |b0|c1|d2|e3| |e1|i1|i2|e2| | |
* |G |H |I |I4| P0 E A I P3 |c0|d1|e2|f3| |e3|i3|i4|e4| | |
* |P2|H5|I5|P3| D H I5 |d0|e1|f2|g3| | |
* G H5 | |
* P2 | |
* | |
* sx, sy | |
* -1 -1 | -2 0 (x+y) (x-y) -3 1 (x+y-1) (x-y+1) | |
* -1 0 | -1 -1 -2 0 | |
* -1 1 | 0 -2 -1 -1 | |
* -1 2 | 1 -3 0 -2 | |
* | |
* 0 -1 | -1 1 (x+y) (x-y) ... ... ... | |
* 0 0 | 0 0 | |
* 0 1 | 1 -1 | |
* 0 2 | 2 -2 | |
* | |
* 1 -1 | 0 2 ... | |
* 1 0 | 1 1 | |
* 1 1 | 2 0 | |
* 1 2 | 3 -1 | |
* | |
* 2 -1 | 1 3 ... | |
* 2 0 | 2 2 | |
* 2 1 | 3 1 | |
* 2 2 | 4 0 | |
*/ | |
private static float diagonal_edge(float mat[][], float[] wp) { | |
float dw1 = wp[0] * (df(mat[0][2], mat[1][1]) + df(mat[1][1], mat[2][0]) + df(mat[1][3], mat[2][2]) + df(mat[2][2], mat[3][1])) + | |
wp[1] * (df(mat[0][3], mat[1][2]) + df(mat[2][1], mat[3][0])) + | |
wp[2] * (df(mat[0][3], mat[2][1]) + df(mat[1][2], mat[3][0])) + | |
wp[3] * df(mat[1][2], mat[2][1]) + | |
wp[4] * (df(mat[0][2], mat[2][0]) + df(mat[1][3], mat[3][1])) + | |
wp[5] * (df(mat[0][1], mat[1][0]) + df(mat[2][3], mat[3][2])); | |
float dw2 = wp[0] * (df(mat[0][1], mat[1][2]) + df(mat[1][2], mat[2][3]) + df(mat[1][0], mat[2][1]) + df(mat[2][1], mat[3][2])) + | |
wp[1] * (df(mat[0][0], mat[1][1]) + df(mat[2][2], mat[3][3])) + | |
wp[2] * (df(mat[0][0], mat[2][2]) + df(mat[1][1], mat[3][3])) + | |
wp[3] * df(mat[1][1], mat[2][2]) + | |
wp[4] * (df(mat[1][0], mat[3][2]) + df(mat[0][1], mat[2][3])) + | |
wp[5] * (df(mat[0][2], mat[1][3]) + df(mat[2][0], mat[3][1])); | |
return (dw1 - dw2); | |
} | |
private static final int f = 2; | |
/** | |
* Super-xBR scaling<br> | |
* perform super-xbr (fast shader version) scaling by factor f=2 only. | |
*/ | |
public static void scaleSuperXBRT(int[] data, int[] out, int w, int h) { | |
int outw = w * f, outh = h * f; | |
float[] wp = { 2.0f, 1.0f, -1.0f, 4.0f, -1.0f, 1.0f }; | |
float[][] r = new float[4][4], g = new float[4][4], b = new float[4][4], a = new float[4][4], Y = new float[4][4]; | |
// First Pass | |
for (int y = 0; y < outh; ++y) { | |
for (int x = 0; x < outw; ++x) { | |
int cx = x / f, cy = y / f; // central pixels on original images | |
// sample supporting pixels in original image | |
for (int sx = -1; sx <= 2; ++sx) { | |
for (int sy = -1; sy <= 2; ++sy) { | |
// clamp pixel locations | |
int csy = clamp(sy + cy, 0, h - 1); | |
int csx = clamp(sx + cx, 0, w - 1); | |
// sample & add weighted components | |
int sample = data[csy * w + csx]; | |
r[sx + 1][sy + 1] = R(sample); | |
g[sx + 1][sy + 1] = G(sample); | |
b[sx + 1][sy + 1] = B(sample); | |
a[sx + 1][sy + 1] = A(sample); | |
Y[sx + 1][sy + 1] = 0.2126f * r[sx + 1][sy + 1] + 0.7152f * g[sx + 1][sy + 1] + 0.0722f * b[sx + 1][sy + 1]; | |
} | |
} | |
float min_r_sample = min4(r[1][1], r[2][1], r[1][2], r[2][2]); | |
float min_g_sample = min4(g[1][1], g[2][1], g[1][2], g[2][2]); | |
float min_b_sample = min4(b[1][1], b[2][1], b[1][2], b[2][2]); | |
float min_a_sample = min4(a[1][1], a[2][1], a[1][2], a[2][2]); | |
float max_r_sample = max4(r[1][1], r[2][1], r[1][2], r[2][2]); | |
float max_g_sample = max4(g[1][1], g[2][1], g[1][2], g[2][2]); | |
float max_b_sample = max4(b[1][1], b[2][1], b[1][2], b[2][2]); | |
float max_a_sample = max4(a[1][1], a[2][1], a[1][2], a[2][2]); | |
float d_edge = diagonal_edge(Y, wp); | |
float r1, g1, b1, a1, r2, g2, b2, a2, rf, gf, bf, af; | |
r1 = w1 * (r[0][3] + r[3][0]) + w2 * (r[1][2] + r[2][1]); | |
g1 = w1 * (g[0][3] + g[3][0]) + w2 * (g[1][2] + g[2][1]); | |
b1 = w1 * (b[0][3] + b[3][0]) + w2 * (b[1][2] + b[2][1]); | |
a1 = w1 * (a[0][3] + a[3][0]) + w2 * (a[1][2] + a[2][1]); | |
r2 = w1 * (r[0][0] + r[3][3]) + w2 * (r[1][1] + r[2][2]); | |
g2 = w1 * (g[0][0] + g[3][3]) + w2 * (g[1][1] + g[2][2]); | |
b2 = w1 * (b[0][0] + b[3][3]) + w2 * (b[1][1] + b[2][2]); | |
a2 = w1 * (a[0][0] + a[3][3]) + w2 * (a[1][1] + a[2][2]); | |
// generate and write result | |
if (d_edge <= 0.0f) { rf = r1; gf = g1; bf = b1; af = a1; } | |
else { rf = r2; gf = g2; bf = b2; af = a2; } | |
// anti-ringing, clamp. | |
rf = clamp(rf, min_r_sample, max_r_sample); | |
gf = clamp(gf, min_g_sample, max_g_sample); | |
bf = clamp(bf, min_b_sample, max_b_sample); | |
af = clamp(af, min_a_sample, max_a_sample); | |
int ri = clamp((int) Math.ceil(rf), 0, 255); | |
int gi = clamp((int) Math.ceil(gf), 0, 255); | |
int bi = clamp((int) Math.ceil(bf), 0, 255); | |
int ai = clamp((int) Math.ceil(af), 0, 255); | |
out[y * outw + x] = out[y * outw + x + 1] = out[(y + 1) * outw + x] = data[cy * w + cx]; | |
out[(y + 1) * outw + x + 1] = (ai << 24) | (bi << 16) | (gi << 8) | ri; | |
++x; | |
} | |
++y; | |
} | |
// Second Pass | |
wp[0] = 2.0f; | |
wp[1] = 0.0f; | |
wp[2] = 0.0f; | |
wp[3] = 0.0f; | |
wp[4] = 0.0f; | |
wp[5] = 0.0f; | |
for (int y = 0; y < outh; ++y) { | |
for (int x = 0; x < outw; ++x) { | |
// sample supporting pixels in original image | |
for (int sx = -1; sx <= 2; ++sx) { | |
for (int sy = -1; sy <= 2; ++sy) { | |
// clamp pixel locations | |
int csy = clamp(sx - sy + y, 0, f * h - 1); | |
int csx = clamp(sx + sy + x, 0, f * w - 1); | |
// sample & add weighted components | |
int sample = out[csy * outw + csx]; | |
r[sx + 1][sy + 1] = R(sample); | |
g[sx + 1][sy + 1] = G(sample); | |
b[sx + 1][sy + 1] = B(sample); | |
a[sx + 1][sy + 1] = A(sample); | |
Y[sx + 1][sy + 1] = 0.2126f * r[sx + 1][sy + 1] + 0.7152f * g[sx + 1][sy + 1] + 0.0722f * b[sx + 1][sy + 1]; | |
} | |
} | |
float min_r_sample = min4(r[1][1], r[2][1], r[1][2], r[2][2]); | |
float min_g_sample = min4(g[1][1], g[2][1], g[1][2], g[2][2]); | |
float min_b_sample = min4(b[1][1], b[2][1], b[1][2], b[2][2]); | |
float min_a_sample = min4(a[1][1], a[2][1], a[1][2], a[2][2]); | |
float max_r_sample = max4(r[1][1], r[2][1], r[1][2], r[2][2]); | |
float max_g_sample = max4(g[1][1], g[2][1], g[1][2], g[2][2]); | |
float max_b_sample = max4(b[1][1], b[2][1], b[1][2], b[2][2]); | |
float max_a_sample = max4(a[1][1], a[2][1], a[1][2], a[2][2]); | |
float d_edge = diagonal_edge(Y, wp); | |
float r1, g1, b1, a1, r2, g2, b2, a2, rf, gf, bf, af; | |
r1 = w3 * (r[0][3] + r[3][0]) + w4 * (r[1][2] + r[2][1]); | |
g1 = w3 * (g[0][3] + g[3][0]) + w4 * (g[1][2] + g[2][1]); | |
b1 = w3 * (b[0][3] + b[3][0]) + w4 * (b[1][2] + b[2][1]); | |
a1 = w3 * (a[0][3] + a[3][0]) + w4 * (a[1][2] + a[2][1]); | |
r2 = w3 * (r[0][0] + r[3][3]) + w4 * (r[1][1] + r[2][2]); | |
g2 = w3 * (g[0][0] + g[3][3]) + w4 * (g[1][1] + g[2][2]); | |
b2 = w3 * (b[0][0] + b[3][3]) + w4 * (b[1][1] + b[2][2]); | |
a2 = w3 * (a[0][0] + a[3][3]) + w4 * (a[1][1] + a[2][2]); | |
// generate and write result | |
if (d_edge <= 0.0f) { rf = r1; gf = g1; bf = b1; af = a1; } | |
else { rf = r2; gf = g2; bf = b2; af = a2; } | |
// anti-ringing, clamp. | |
rf = clamp(rf, min_r_sample, max_r_sample); | |
gf = clamp(gf, min_g_sample, max_g_sample); | |
bf = clamp(bf, min_b_sample, max_b_sample); | |
af = clamp(af, min_a_sample, max_a_sample); | |
int ri = clamp((int) Math.ceil(rf), 0, 255); | |
int gi = clamp((int) Math.ceil(gf), 0, 255); | |
int bi = clamp((int) Math.ceil(bf), 0, 255); | |
int ai = clamp((int) Math.ceil(af), 0, 255); | |
out[y * outw + x + 1] = (ai << 24) | (bi << 16) | (gi << 8) | ri; | |
for (int sx = -1; sx <= 2; ++sx) { | |
for (int sy = -1; sy <= 2; ++sy) { | |
// clamp pixel locations | |
int csy = clamp(sx - sy + 1 + y, 0, f * h - 1); | |
int csx = clamp(sx + sy - 1 + x, 0, f * w - 1); | |
// sample & add weighted components | |
int sample = out[csy * outw + csx]; | |
r[sx + 1][sy + 1] = R(sample); | |
g[sx + 1][sy + 1] = G(sample); | |
b[sx + 1][sy + 1] = B(sample); | |
a[sx + 1][sy + 1] = A(sample); | |
Y[sx + 1][sy + 1] = 0.2126f * r[sx + 1][sy + 1] + 0.7152f * g[sx + 1][sy + 1] + 0.0722f * b[sx + 1][sy + 1]; | |
} | |
} | |
d_edge = diagonal_edge(Y, wp); | |
r1 = w3 * (r[0][3] + r[3][0]) + w4 * (r[1][2] + r[2][1]); | |
g1 = w3 * (g[0][3] + g[3][0]) + w4 * (g[1][2] + g[2][1]); | |
b1 = w3 * (b[0][3] + b[3][0]) + w4 * (b[1][2] + b[2][1]); | |
a1 = w3 * (a[0][3] + a[3][0]) + w4 * (a[1][2] + a[2][1]); | |
r2 = w3 * (r[0][0] + r[3][3]) + w4 * (r[1][1] + r[2][2]); | |
g2 = w3 * (g[0][0] + g[3][3]) + w4 * (g[1][1] + g[2][2]); | |
b2 = w3 * (b[0][0] + b[3][3]) + w4 * (b[1][1] + b[2][2]); | |
a2 = w3 * (a[0][0] + a[3][3]) + w4 * (a[1][1] + a[2][2]); | |
// generate and write result | |
if (d_edge <= 0.0f) { rf = r1; gf = g1; bf = b1; af = a1; } | |
else { rf = r2; gf = g2; bf = b2; af = a2; } | |
// anti-ringing, clamp. | |
rf = clamp(rf, min_r_sample, max_r_sample); | |
gf = clamp(gf, min_g_sample, max_g_sample); | |
bf = clamp(bf, min_b_sample, max_b_sample); | |
af = clamp(af, min_a_sample, max_a_sample); | |
ri = clamp((int) (Math.ceil(rf)), 0, 255); | |
gi = clamp((int) (Math.ceil(gf)), 0, 255); | |
bi = clamp((int) (Math.ceil(bf)), 0, 255); | |
ai = clamp((int) (Math.ceil(af)), 0, 255); | |
out[(y + 1) * outw + x] = (ai << 24) | (bi << 16) | (gi << 8) | ri; | |
++x; | |
} | |
++y; | |
} | |
// Third Pass | |
wp[0] = 2.0f; | |
wp[1] = 1.0f; | |
wp[2] = -1.0f; | |
wp[3] = 4.0f; | |
wp[4] = -1.0f; | |
wp[5] = 1.0f; | |
for (int y = outh - 1; y >= 0; --y) { | |
for (int x = outw - 1; x >= 0; --x) { | |
for (int sx = -2; sx <= 1; ++sx) { | |
for (int sy = -2; sy <= 1; ++sy) { | |
// clamp pixel locations | |
int csy = clamp(sy + y, 0, f * h - 1); | |
int csx = clamp(sx + x, 0, f * w - 1); | |
// sample & add weighted components | |
int sample = out[csy * outw + csx]; | |
r[sx + 2][sy + 2] = R(sample); | |
g[sx + 2][sy + 2] = G(sample); | |
b[sx + 2][sy + 2] = B(sample); | |
a[sx + 2][sy + 2] = A(sample); | |
Y[sx + 2][sy + 2] = 0.2126f * r[sx + 2][sy + 2] + 0.7152f * g[sx + 2][sy + 2] + 0.0722f * b[sx + 2][sy + 2]; | |
} | |
} | |
float min_r_sample = min4(r[1][1], r[2][1], r[1][2], r[2][2]); | |
float min_g_sample = min4(g[1][1], g[2][1], g[1][2], g[2][2]); | |
float min_b_sample = min4(b[1][1], b[2][1], b[1][2], b[2][2]); | |
float min_a_sample = min4(a[1][1], a[2][1], a[1][2], a[2][2]); | |
float max_r_sample = max4(r[1][1], r[2][1], r[1][2], r[2][2]); | |
float max_g_sample = max4(g[1][1], g[2][1], g[1][2], g[2][2]); | |
float max_b_sample = max4(b[1][1], b[2][1], b[1][2], b[2][2]); | |
float max_a_sample = max4(a[1][1], a[2][1], a[1][2], a[2][2]); | |
float d_edge = diagonal_edge(Y, wp); | |
float r1, g1, b1, a1, r2, g2, b2, a2, rf, gf, bf, af; | |
r1 = w1 * (r[0][3] + r[3][0]) + w2 * (r[1][2] + r[2][1]); | |
g1 = w1 * (g[0][3] + g[3][0]) + w2 * (g[1][2] + g[2][1]); | |
b1 = w1 * (b[0][3] + b[3][0]) + w2 * (b[1][2] + b[2][1]); | |
a1 = w1 * (a[0][3] + a[3][0]) + w2 * (a[1][2] + a[2][1]); | |
r2 = w1 * (r[0][0] + r[3][3]) + w2 * (r[1][1] + r[2][2]); | |
g2 = w1 * (g[0][0] + g[3][3]) + w2 * (g[1][1] + g[2][2]); | |
b2 = w1 * (b[0][0] + b[3][3]) + w2 * (b[1][1] + b[2][2]); | |
a2 = w1 * (a[0][0] + a[3][3]) + w2 * (a[1][1] + a[2][2]); | |
// generate and write result | |
if (d_edge <= 0.0f) { rf = r1; gf = g1; bf = b1; af = a1; } | |
else { rf = r2; gf = g2; bf = b2; af = a2; } | |
// anti-ringing, clamp. | |
rf = clamp(rf, min_r_sample, max_r_sample); | |
gf = clamp(gf, min_g_sample, max_g_sample); | |
bf = clamp(bf, min_b_sample, max_b_sample); | |
af = clamp(af, min_a_sample, max_a_sample); | |
int ri = clamp((int) Math.ceil(rf), 0, 255); | |
int gi = clamp((int) Math.ceil(gf), 0, 255); | |
int bi = clamp((int) Math.ceil(bf), 0, 255); | |
int ai = clamp((int) Math.ceil(af), 0, 255); | |
out[y * outw + x] = (ai << 24) | (bi << 16) | (gi << 8) | ri; | |
} | |
} | |
} | |
} |
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
//package ; | |
import java.awt.Graphics; | |
import java.awt.Image; | |
import java.awt.image.BufferedImage; | |
public class SuperXBRRescale { | |
public static BufferedImage scale2xBR(BufferedImage source) { | |
int width = source.getWidth(); | |
int height = source.getHeight(); | |
int[] inPixels = new int[width * height]; | |
source.getRGB(0, 0, width, height, inPixels, 0, width); | |
final int factor = 2; | |
int destWidth = width * factor; | |
int destHeight = width * factor; | |
int[] outPixels = new int[destWidth * destHeight]; | |
SuperXBR.scaleSuperXBRT(inPixels, outPixels, width, height); | |
BufferedImage dest = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_ARGB); | |
dest.setRGB(0, 0, destWidth, destHeight, outPixels, 0, destWidth); | |
return dest; | |
} | |
public static BufferedImage scale2xBR(Image source) { | |
BufferedImage buffered = new BufferedImage(source.getWidth(null), | |
source.getHeight(null), | |
BufferedImage.TYPE_INT_ARGB); | |
Graphics g = buffered.getGraphics(); | |
g.drawImage(source, 0, 0, null); | |
g.dispose(); | |
return scale2xBR(buffered); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment