Skip to content

Instantly share code, notes, and snippets.

@stanio
Last active October 18, 2022 01:24
Show Gist options
  • Save stanio/a0483c34cfe97ae9483bfd553a1a794d to your computer and use it in GitHub Desktop.
Save stanio/a0483c34cfe97ae9483bfd553a1a794d to your computer and use it in GitHub Desktop.
Super-xBR Scaler: https://pastebin.com/cbH8ZQQT (Java version)
/*
* Copyright (c) 2016 Hyllian - sergiogdb@gmail.com
*
* 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;
}
}
}
}
//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