mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
nQuant: refresh to mcychan/nQuant.android@12822f15c9
This commit is contained in:
committed by
José Rebelo
parent
26cf09d700
commit
c8da8b4717
@@ -3,17 +3,140 @@ package com.android.nQuant;
|
||||
import android.graphics.Color;
|
||||
|
||||
public class BitmapUtilities {
|
||||
static final char BYTE_MAX = -Byte.MIN_VALUE + Byte.MAX_VALUE;
|
||||
static final char BYTE_MAX = -Byte.MIN_VALUE + Byte.MAX_VALUE;
|
||||
|
||||
static int getColorIndex(final int c, boolean hasSemiTransparency, boolean hasTransparency) {
|
||||
if (hasSemiTransparency)
|
||||
return (Color.alpha(c) & 0xF0) << 8 | (Color.red(c) & 0xF0) << 4 | (Color.green(c) & 0xF0) | (Color.blue(c) >> 4);
|
||||
if (hasTransparency)
|
||||
return (Color.alpha(c) & 0x80) << 8 | (Color.red(c) & 0xF8) << 7 | (Color.green(c) & 0xF8) << 2 | (Color.blue(c) >> 3);
|
||||
return (Color.red(c) & 0xF8) << 8 | (Color.green(c) & 0xFC) << 3 | (Color.blue(c) >> 3);
|
||||
}
|
||||
static int getColorIndex(final int c, boolean hasSemiTransparency, boolean hasTransparency)
|
||||
{
|
||||
if(hasSemiTransparency)
|
||||
return (Color.alpha(c) & 0xF0) << 8 | (Color.red(c) & 0xF0) << 4 | (Color.green(c) & 0xF0) | (Color.blue(c) >> 4);
|
||||
if (hasTransparency)
|
||||
return (Color.alpha(c) & 0x80) << 8 | (Color.red(c) & 0xF8) << 7 | (Color.green(c) & 0xF8) << 2 | (Color.blue(c) >> 3);
|
||||
return (Color.red(c) & 0xF8) << 8 | (Color.green(c) & 0xFC) << 3 | (Color.blue(c) >> 3);
|
||||
}
|
||||
|
||||
static double sqr(double value) {
|
||||
return value * value;
|
||||
}
|
||||
static double sqr(double value)
|
||||
{
|
||||
return value * value;
|
||||
}
|
||||
|
||||
static int[] calcDitherPixel(int c, int[] clamp, int[] rowerr, int cursor, boolean noBias)
|
||||
{
|
||||
int[] ditherPixel = new int[4];
|
||||
if (noBias) {
|
||||
ditherPixel[0] = clamp[((rowerr[cursor] + 0x1008) >> 4) + Color.red(c)];
|
||||
ditherPixel[1] = clamp[((rowerr[cursor + 1] + 0x1008) >> 4) + Color.green(c)];
|
||||
ditherPixel[2] = clamp[((rowerr[cursor + 2] + 0x1008) >> 4) + Color.blue(c)];
|
||||
ditherPixel[3] = clamp[((rowerr[cursor + 3] + 0x1008) >> 4) + Color.alpha(c)];
|
||||
return ditherPixel;
|
||||
}
|
||||
|
||||
ditherPixel[0] = clamp[((rowerr[cursor] + 0x2010) >> 5) + Color.red(c)];
|
||||
ditherPixel[1] = clamp[((rowerr[cursor + 1] + 0x1008) >> 4) + Color.green(c)];
|
||||
ditherPixel[2] = clamp[((rowerr[cursor + 2] + 0x2010) >> 5) + Color.blue(c)];
|
||||
ditherPixel[3] = Color.alpha(c);
|
||||
return ditherPixel;
|
||||
}
|
||||
|
||||
static int[] quantize_image(final int width, final int height, final int[] pixels, final Integer[] palette, final Ditherable ditherable, final boolean hasSemiTransparency, final boolean dither)
|
||||
{
|
||||
int[] qPixels = new int[pixels.length];
|
||||
int nMaxColors = palette.length;
|
||||
|
||||
int pixelIndex = 0;
|
||||
if (dither) {
|
||||
final int DJ = 4;
|
||||
final int BLOCK_SIZE = 256;
|
||||
final int DITHER_MAX = 20;
|
||||
final int err_len = (width + 2) * DJ;
|
||||
int[] clamp = new int[DJ * BLOCK_SIZE];
|
||||
int[] limtb = new int[2 * BLOCK_SIZE];
|
||||
|
||||
for (short i = 0; i < BLOCK_SIZE; ++i) {
|
||||
clamp[i] = 0;
|
||||
clamp[i + BLOCK_SIZE] = i;
|
||||
clamp[i + BLOCK_SIZE * 2] = BYTE_MAX;
|
||||
clamp[i + BLOCK_SIZE * 3] = BYTE_MAX;
|
||||
|
||||
limtb[i] = -DITHER_MAX;
|
||||
limtb[i + BLOCK_SIZE] = DITHER_MAX;
|
||||
}
|
||||
for (short i = -DITHER_MAX; i <= DITHER_MAX; ++i)
|
||||
limtb[i + BLOCK_SIZE] = i % 4 == 3 ? 0 : i;
|
||||
|
||||
boolean noBias = hasSemiTransparency || nMaxColors < 64;
|
||||
int dir = 1;
|
||||
int[] row0 = new int[err_len];
|
||||
int[] row1 = new int[err_len];
|
||||
int[] lookup = new int[65536];
|
||||
for (int i = 0; i < height; ++i) {
|
||||
if (dir < 0)
|
||||
pixelIndex += width - 1;
|
||||
|
||||
int cursor0 = DJ, cursor1 = width * DJ;
|
||||
row1[cursor1] = row1[cursor1 + 1] = row1[cursor1 + 2] = row1[cursor1 + 3] = 0;
|
||||
for (int j = 0; j < width; ++j) {
|
||||
int c = pixels[pixelIndex];
|
||||
int[] ditherPixel = calcDitherPixel(c, clamp, row0, cursor0, noBias);
|
||||
int r_pix = ditherPixel[0];
|
||||
int g_pix = ditherPixel[1];
|
||||
int b_pix = ditherPixel[2];
|
||||
int a_pix = ditherPixel[3];
|
||||
|
||||
int c1 = Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
if(noBias && a_pix > 0xF0) {
|
||||
int offset = ditherable.getColorIndex(c1);
|
||||
if (lookup[offset] == 0)
|
||||
lookup[offset] = (Color.alpha(c) == 0) ? 1 : ditherable.nearestColorIndex(palette, c1, i + j) + 1;
|
||||
qPixels[pixelIndex] = palette[lookup[offset] - 1];
|
||||
}
|
||||
else {
|
||||
short qIndex = (Color.alpha(c) == 0) ? 0 : ditherable.nearestColorIndex(palette, c1, i + j);
|
||||
qPixels[pixelIndex] = palette[qIndex];
|
||||
}
|
||||
|
||||
int c2 = qPixels[pixelIndex];
|
||||
r_pix = limtb[r_pix - Color.red(c2) + BLOCK_SIZE];
|
||||
g_pix = limtb[g_pix - Color.green(c2) + BLOCK_SIZE];
|
||||
b_pix = limtb[b_pix - Color.blue(c2) + BLOCK_SIZE];
|
||||
a_pix = limtb[a_pix - Color.alpha(c2) + BLOCK_SIZE];
|
||||
|
||||
int k = r_pix * 2;
|
||||
row1[cursor1 - DJ] = r_pix;
|
||||
row1[cursor1 + DJ] += (r_pix += k);
|
||||
row1[cursor1] += (r_pix += k);
|
||||
row0[cursor0 + DJ] += (r_pix + k);
|
||||
|
||||
k = g_pix * 2;
|
||||
row1[cursor1 + 1 - DJ] = g_pix;
|
||||
row1[cursor1 + 1 + DJ] += (g_pix += k);
|
||||
row1[cursor1 + 1] += (g_pix += k);
|
||||
row0[cursor0 + 1 + DJ] += (g_pix + k);
|
||||
|
||||
k = b_pix * 2;
|
||||
row1[cursor1 + 2 - DJ] = b_pix;
|
||||
row1[cursor1 + 2 + DJ] += (b_pix += k);
|
||||
row1[cursor1 + 2] += (b_pix += k);
|
||||
row0[cursor0 + 2 + DJ] += (b_pix + k);
|
||||
|
||||
k = a_pix * 2;
|
||||
row1[cursor1 + 3 - DJ] = a_pix;
|
||||
row1[cursor1 + 3 + DJ] += (a_pix += k);
|
||||
row1[cursor1 + 3] += (a_pix += k);
|
||||
row0[cursor0 + 3 + DJ] += (a_pix + k);
|
||||
|
||||
cursor0 += DJ;
|
||||
cursor1 -= DJ;
|
||||
pixelIndex += dir;
|
||||
}
|
||||
if ((i % 2) == 1)
|
||||
pixelIndex += width + 1;
|
||||
|
||||
dir *= -1;
|
||||
int[] temp = row0; row0 = row1; row1 = temp;
|
||||
}
|
||||
return qPixels;
|
||||
}
|
||||
|
||||
return qPixels;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,206 +8,216 @@ package com.android.nQuant;
|
||||
import android.graphics.Color;
|
||||
|
||||
public class BlueNoise {
|
||||
// Reference mask from: https://tellusim.com/download/noise/64x64_l64_s16.png
|
||||
// Made from: https://github.com/Tellusim/BlueNoise
|
||||
static final byte[] TELL_BLUE_NOISE = {
|
||||
18, 60, -13, 24, -93, -44, -107, 18, -34, 123, -120, 63, 115, 32, 91, -28, 118, -12, 97, -42, 77, -76, 30, -97, -39,
|
||||
87, -10, -57, 54, -3, -55, 118, 10, -35, 42, 124, -97, 57, -60, 1, -41, -103, -68, 97, -116, 44, -74, 60, -48, 87,
|
||||
-9, -117, 10, -73, 98, 40, -77, 55, -17, -86, 65, 36, 114, -48, -93, -39, -121, 98, -21, 119, 37, 84, -100, -2, 38,
|
||||
-26, -89, -10, -124, 13, -66, -115, 34, -89, -5, 121, -51, 98, 38, -118, 65, -88, 113, -124, 60, -18, -77, 101, -128, 2,
|
||||
-37, 95, -118, 86, 25, 58, -24, 69, -47, -9, 81, -127, 125, -77, 41, 111, -49, 53, -22, -108, 79, -61, 21, 126, -7,
|
||||
-128, -71, 71, 107, 36, 81, -56, 52, -80, -5, -55, 47, -77, 99, -57, 27, 76, -41, 106, 43, 78, -34, 107, 53, -123,
|
||||
21, -16, -70, 124, -29, 24, -48, 8, 96, -102, 78, -49, 25, 77, -70, 12, 47, -51, -80, 102, 13, -87, 36, 107, -85,
|
||||
27, -28, 10, -99, -24, 73, -101, 123, 15, -38, 97, -113, -51, 81, -34, 46, -20, 8, -82, -4, -100, 11, 74, -128, 112,
|
||||
-23, 81, 9, -112, 111, -79, 50, -103, -24, -81, 19, -99, -22, -65, 70, -101, 52, 5, -99, 84, 50, -78, -33, 39, -5,
|
||||
56, -93, -16, 111, -86, -27, 122, -2, -123, -53, 120, -106, -55, -16, 66, -59, 55, 105, -69, 29, -5, -57, 55, -94, 3,
|
||||
60, 31, -96, 17, 92, -109, -50, 124, 49, -30, 110, -58, 34, -81, 62, -105, -39, 48, -22, 14, -49, 87, 0, 126, -56,
|
||||
84, 31, 96, -8, 110, -47, 91, -67, -4, -116, 102, 68, -60, -117, 119, -64, 45, -117, 26, 69, -105, 45, 77, -15, 29,
|
||||
72, 8, 100, -98, 91, -113, -15, 82, -120, 108, -84, 90, -16, 117, -75, -20, 111, -57, -80, 65, 30, -120, -62, 69, -108,
|
||||
-9, 101, 11, -46, 30, 125, -85, 94, -119, 70, -83, 33, -126, 56, -4, -111, -42, -86, 20, -121, 35, -30, 116, 23, -21,
|
||||
-93, 11, 85, -40, 3, 75, -33, 100, -66, 8, -45, -84, 92, -76, -35, -122, 46, -65, -6, 31, -49, 49, 11, -27, 47,
|
||||
18, -125, -53, 67, -107, 51, -3, 102, -26, 86, 0, 97, 15, -72, 43, -35, -114, 70, -75, -9, 72, -47, 30, -7, 108,
|
||||
-42, 95, -25, -78, 115, 40, 68, -21, 80, -79, 57, -107, -53, 44, 127, -32, 30, -104, 107, -85, 20, -99, -13, 112, 67,
|
||||
24, -113, 1, 111, 22, -40, 121, 68, -79, 113, -97, -65, 69, -105, -42, 80, 28, -8, 87, -39, -124, 16, -93, 42, -38,
|
||||
-99, -23, 120, 66, -86, 93, -19, 103, -117, 10, -93, 62, -67, -103, 13, -89, 30, 76, 2, -64, -104, 123, -61, 14, 98,
|
||||
0, 71, -69, -125, 78, -79, 57, -14, 48, -45, 78, 36, -126, -73, -26, 105, 45, -61, 70, -83, 10, -116, -19, 57, 4,
|
||||
92, -35, 119, 1, -88, 110, -100, -60, 36, 122, 63, -58, 116, -73, 63, 24, -122, -51, 5, 38, -59, 27, -37, 50, 99,
|
||||
-38, 120, -21, 53, 86, -57, -118, -34, 101, 11, -32, 53, -103, -38, -85, 110, -6, 61, -50, 5, 100, -63, -120, 125, -90,
|
||||
-35, 57, 2, 73, -51, -90, -8, -109, 90, -31, 41, 95, -51, -126, 27, -81, 39, -59, 65, -34, 9, 101, -88, -24, -102,
|
||||
-13, -116, 4, 106, -62, 80, -11, 108, -109, 86, -89, 117, -65, -3, -123, 24, 77, -113, -35, 122, 7, 71, -89, 47, -128,
|
||||
103, -9, 64, 20, -45, -101, 17, 108, -112, -35, 15, 59, -25, 9, 97, -60, 116, -104, -3, 58, 126, 21, -54, 59, -98,
|
||||
-69, -2, 124, -24, 83, -111, 95, 20, -120, 54, -44, 25, 91, 13, 77, -39, 51, -102, -27, 39, -95, 56, -64, -15, 13,
|
||||
58, -101, 41, 91, -84, -58, -3, 31, -96, 41, -63, -5, 89, -45, 24, -90, 120, -118, 89, 52, -32, -67, 55, 82, -97,
|
||||
103, -80, 73, -111, 26, -81, 38, 92, -118, -32, -78, 99, -1, 115, 35, 74, -84, 61, -54, 3, -68, -18, 127, -78, 72,
|
||||
-114, -48, -83, 35, -64, 96, -6, 85, -72, 124, -37, 18, 70, -126, -29, 80, -47, -15, 12, 112, 56, -71, 75, -31, 108,
|
||||
-106, 33, -72, 84, -20, -52, 42, -23, -82, 115, 28, -90, -11, 35, -46, -3, 41, -50, -11, 82, -35, -62, 25, 82, 51,
|
||||
-106, -67, -22, -116, -38, 16, -104, 32, 109, 59, -86, 34, 4, -28, 104, 48, -15, 113, 18, -90, 37, -127, -12, 23, -112,
|
||||
91, -79, 110, 2, -87, 127, -111, 75, -34, -108, 102, -10, -121, 52, -40, 126, -10, -116, 57, 93, -70, 2, 70, -123, -4,
|
||||
97, -59, 119, -127, 87, -97, 122, 48, -123, 10, 117, -93, -2, -48, 18, 56, 86, 24, 110, -75, 78, -14, -123, -34, 84,
|
||||
-106, 91, -64, -3, -93, 84, -120, -17, 76, -43, 104, 60, -60, 49, -4, -42, 45, -61, 60, 21, -66, 50, -87, 7, -58,
|
||||
25, 94, -74, 18, -94, 67, 20, -80, 6, -111, 100, 26, -49, 49, -28, -108, 16, 68, -23, 14, -70, -26, 96, -72, 64,
|
||||
-27, 94, -128, 120, -33, -86, -58, -12, 40, -50, 121, -70, 48, -2, -52, 51, -126, 120, 29, -46, 61, 126, -65, 10, -94,
|
||||
-30, 87, -81, 113, -108, 13, 98, -118, -36, 106, -21, 31, 121, 71, -86, -26, 61, -11, 98, -62, -31, 118, 46, -59, -29,
|
||||
-88, 122, -100, 89, 62, -16, -90, -55, 95, 55, -102, 29, -7, -108, 33, -62, 58, 4, -104, 67, 104, -122, 86, -96, 28,
|
||||
5, 98, -96, 115, 6, -30, 60, -72, 8, -95, -27, -115, 49, 114, 21, -118, 8, -29, 69, -71, -17, 79, 7, -105, 81,
|
||||
-51, -127, -15, 45, -109, 115, -50, -125, 40, 74, -108, -14, 107, 9, 72, -14, 11, -78, -49, 30, 115, 42, -116, -5, 114,
|
||||
-61, 86, -40, 111, -5, -82, 80, -22, 31, -43, 52, -3, -33, 66, -115, -56, -21, 32, -78, 96, -102, -14, 83, 35, 96,
|
||||
68, -13, -75, -52, 63, 101, -62, 24, 122, -98, 31, -56, 53, -85, -5, 61, -61, 99, 16, -80, 34, 86, 4, -81, -42,
|
||||
81, -98, 44, -121, -64, 108, 43, 83, -104, -64, 4, 74, -42, -89, 64, 7, -80, 63, -115, 45, -58, 106, -82, 10, -103,
|
||||
118, -81, 89, -13, 44, 80, -111, 63, -50, 21, 106, -119, -55, -87, 14, -101, 82, 35, -12, -91, 49, -125, -23, 56, -74,
|
||||
109, -25, 91, 21, 114, -96, -2, -41, 64, -7, -103, -30, 114, 52, 16, -69, -37, 98, 34, -43, -115, -25, 14, 97, -29,
|
||||
-78, 103, 34, -32, -123, 100, 12, -43, 123, 13, -112, 55, -51, 73, 35, -45, 23, -94, 124, -77, 5, -27, 77, -84, -31,
|
||||
63, -1, 42, -45, 121, -28, -114, 110, -41, 88, -1, -59, 101, 1, -120, 36, -66, -112, -36, 49, 84, -120, 124, -61, 93,
|
||||
22, -55, -121, -11, 124, 60, -5, -92, 68, 0, 125, -87, 33, -125, 55, 7, -105, 127, 53, -17, -74, 36, -95, -35, 86,
|
||||
-21, 112, -75, -15, -127, 107, -27, 16, -44, 98, 45, -124, 122, 43, -67, 112, -108, 58, -71, 19, 66, -74, 27, -104, 72,
|
||||
26, -99, 75, -38, 121, -11, 73, 15, -90, -50, 38, -85, -18, 59, -87, 104, 72, -83, 27, -111, -55, 22, 105, -76, 58,
|
||||
-47, 68, -14, 91, -58, -9, -77, 15, -107, 62, 94, 1, 59, -66, 39, -117, 18, 84, 43, -61, 63, -113, 70, -93, -59,
|
||||
11, -44, 2, -99, -20, 88, -12, 102, -96, -8, 90, -21, 125, -69, -27, 51, -72, 22, -101, 44, -74, 95, -13, 109, 3,
|
||||
83, 32, -115, 5, -34, 35, -22, -63, 95, 71, -30, -128, -11, 27, -105, 100, -65, -99, 38, 68, 90, -52, 111, -30, -63,
|
||||
-125, 116, -90, -1, 97, -38, -87, -2, 93, -83, 38, -12, 116, 29, 85, -76, 101, 67, 24, -48, -128, 34, -41, 52, -121,
|
||||
-54, 11, 44, -116, 115, -18, 93, -46, 85, -23, -122, 55, -69, -110, -34, -75, 101, 69, -69, -106, 111, 50, -6, -95, 38,
|
||||
90, -60, 112, -31, 20, -6, 121, -39, -121, -23, 47, -84, 31, 83, -8, 29, -33, 76, -62, 55, 126, -111, -28, 10, 103,
|
||||
-74, -36, -110, -16, 37, -116, -33, -80, 127, -5, 78, -88, 118, 3, 65, -95, 102, -6, -83, 63, -128, 1, -87, 31, 126,
|
||||
-42, 19, 62, 119, 12, -52, -14, 40, 85, 7, -123, -47, 119, -71, 3, 69, -92, 51, -120, 73, -82, 2, 103, 23, -107,
|
||||
76, 5, -98, -46, 100, -110, 37, -103, 9, -71, 47, 78, -53, -123, 26, 88, 60, -86, 113, 6, 91, 42, -102, 47, -64,
|
||||
20, -25, -69, 93, -18, -61, 78, 18, -63, 33, 112, 66, -57, 3, -84, 92, -26, -94, 49, -127, 114, -93, -40, -78, 75,
|
||||
17, 56, -24, -113, 32, -15, -70, 104, -54, 40, 65, -93, -56, 113, -19, -54, 125, 60, -76, 4, 89, -18, 108, -36, 17,
|
||||
-91, 119, 57, -10, -62, 16, -46, 65, -59, -91, -23, 85, -44, 109, -117, 86, 38, -112, 21, 58, -105, -40, 99, -33, -94,
|
||||
-17, -114, 77, 36, -109, 4, -54, 100, -30, 17, 77, -3, 127, -25, -66, -101, 98, 73, -54, 124, 59, 11, -17, -113, -37,
|
||||
88, 7, 57, -81, 44, -115, 16, -27, 49, -53, -88, 43, -120, 69, -6, -40, -82, 99, -108, 123, -13, -124, -1, 74, 31,
|
||||
10, -90, -10, 66, -38, -86, 112, -73, -32, 124, 28, -113, 73, 0, 40, 104, -28, -61, 109, 67, 24, -79, 56, -102, -63,
|
||||
35, -112, 53, 106, 37, -4, -76, 4, -109, -39, -91, 75, 116, 25, -73, -25, -127, 100, -7, 80, -85, 96, -124, 121, -7,
|
||||
84, -44, 105, -103, 14, 40, -26, 48, -78, 79, 41, 108, -49, -112, -31, 94, 27, -74, 51, 14, -15, 47, 81, -92, -8,
|
||||
57, -70, 120, -52, -82, 10, 51, -90, -19, -118, 88, -3, 110, -19, 67, -45, 0, -90, -38, -126, 52, 102, 22, 89, 37,
|
||||
-67, -3, -106, 52, 121, 27, -34, -73, 34, -38, 0, 68, -77, 19, -100, 29, -62, 54, 96, -113, 77, -55, 17, -32, -95,
|
||||
-68, 22, 120, 46, -125, -54, 123, -109, 89, -53, -121, -2, -67, 107, -45, 22, -105, 54, 89, -124, 117, -46, 82, 44, -40,
|
||||
-68, 29, -123, 102, -74, 92, 23, 67, 121, -28, -64, -91, -13, -124, 62, -49, 95, -17, -91, -57, 59, 92, -111, 117, -60,
|
||||
26, -36, 62, -19, 116, -8, -87, -29, -68, -8, 94, -117, 105, 32, 71, -11, -85, 85, 3, 71, -25, 6, -76, 59, 118,
|
||||
24, 74, -20, -126, 69, -5, -40, 23, -68, -2, 32, -106, -7, 123, -98, 83, -31, 5, 44, -110, -16, -60, -94, -1, 79,
|
||||
48, 119, -37, 104, 20, -79, 74, 17, 107, -100, 8, -21, 49, -97, 106, -114, 91, -68, -128, 67, 28, 108, 63, 22, -84,
|
||||
54, 0, -50, -108, 102, -43, -17, -70, 40, -87, 108, 33, -26, -89, -42, -101, 37, 90, -75, 109, -99, 65, -27, 86, -85,
|
||||
99, -75, 39, -12, 54, -62, -93, 122, -39, 82, 39, 94, 26, -115, -49, 13, -80, -2, -110, 50, -44, -119, -24, 42, -51,
|
||||
79, -70, -14, 56, 5, -47, 37, 82, -39, -99, 4, -121, -40, 126, -26, -77, 115, 15, 53, -100, 34, 117, -105, 78, -50,
|
||||
-116, 87, 6, 101, 63, -57, -12, 28, -48, 5, 127, -112, 47, -44, 14, 68, -52, -114, 104, 12, 73, 25, -82, -6, -119,
|
||||
-67, -35, 111, 40, -104, 56, 86, -52, 127, -4, 92, 64, -77, 123, -123, 13, 99, -55, -92, 125, 9, -82, -5, 119, -53,
|
||||
89, 59, -105, 38, 84, -126, -21, -58, 93, -119, -37, 17, -20, 49, -4, -73, 46, -122, -1, -89, 100, -113, 72, -89, 32,
|
||||
-59, -4, 111, -126, -18, 114, 28, -76, -22, -121, -48, 87, 46, 117, 15, 64, -87, -11, 79, -68, -17, 33, -99, -67, 26,
|
||||
-101, -5, 31, -28, 59, -106, 40, 69, -19, -110, 96, 58, -69, 35, -14, -72, 7, -61, -6, 50, 75, -89, 7, 66, 88,
|
||||
-73, 105, -85, 71, 125, -24, -55, 110, 20, -34, 41, -7, 102, -20, 89, -97, 72, -67, 58, -92, -37, 81, 47, 106, 0,
|
||||
-100, -33, -60, -102, -20, 97, -56, 17, 119, -121, 99, 3, 78, -36, 106, -56, 88, -84, 113, -43, -1, -72, 88, -51, 21,
|
||||
-33, -118, 84, -92, 106, 47, 91, -90, -48, 12, 124, -31, -96, -53, 38, -113, 8, -46, -103, 23, 80, -108, 68, -79, 117,
|
||||
-70, -42, -117, 59, 6, -38, 34, -9, 92, 3, -108, -44, -86, 39, 112, 25, 65, 91, 36, -118, 58, -98, -30, 66, -39,
|
||||
-88, 43, -128, 16, 66, -113, 10, -62, 28, 102, -125, 3, 54, -95, 108, 50, 15, -45, -2, -122, -26, 31, 103, -115, -66,
|
||||
45, 20, -12, 95, -27, 113, 33, 65, -70, -9, 38, -40, 6, -122, 75, 49, 19, -65, 98, -83, 119, -116, -57, 41, 125,
|
||||
17, 72, -24, -64, -127, -3, -89, -54, 8, 110, -13, 41, -78, 26, 87, -63, 116, -19, -73, -12, 79, 54, -103, -18, 77,
|
||||
-66, 118, -20, -74, -11, -101, 118, 54, -59, 112, -98, -15, 76, 0, 101, -124, 70, -92, 54, -67, -127, -23, 103, -95, 121,
|
||||
-72, 92, 30, -27, -96, 122, -17, -109, 51, -25, 13, 67, -94, -15, -68, -116, 99, 13, 83, -32, 126, 57, -35, -81, 76,
|
||||
-60, 114, -114, 6, -32, 62, -95, 45, 122, -41, -77, 110, -54, 45, 11, -107, 32, 64, 88, -63, 24, -108, 80, 2, 48,
|
||||
-54, 34, -91, -44, 121, -57, 22, -38, 89, -3, 81, -54, 19, -28, 71, -98, -49, 105, 8, -56, 81, 27, -50, 75, -77,
|
||||
103, -36, 60, 108, 32, -39, -92, 42, -67, 16, -105, 94, 36, -125, 5, 53, -23, 100, -106, 20, 102, -58, 17, -107, 38,
|
||||
1, 64, -90, -27, 98, -43, -124, 12, -33, 97, -8, -42, -88, 72, -109, 113, -26, 50, -94, -8, 103, -108, 46, -83, 38,
|
||||
-103, 61, -120, 25, -10, 60, -79, 67, -128, 1, -86, 114, -103, -13, 32, -125, 9, -49, -99, 81, 1, 113, -112, 86, -9,
|
||||
-65, -19, 83, -47, -93, 73, -74, 50, -47, -6, -119, 83, -11, 99, -62, -120, 127, 29, -75, 77, -12, 122, -90, 57, -74,
|
||||
42, 126, -19, 12, -71, 73, 5, 83, 39, -71, -16, 126, 9, -42, 114, -1, 94, -53, 117, -112, 29, -18, 106, 43, -34,
|
||||
54, 15, 87, -69, 113, -82, 90, -7, 50, -60, -23, 66, -39, 45, 110, -100, 19, 120, 34, -54, 12, 125, -85, 89, 54,
|
||||
-36, -84, 54, -25, 84, -9, -53, 60, -101, 39, -69, 22, -110, 79, 6, -128, -52, 96, 37, -114, -52, -29, -119, 88, 26,
|
||||
-56, -117, 70, -84, -35, -96, 54, -75, 9, 91, -71, -44, -102, 80, -68, -119, -40, 42, -23, 19, 57, -66, -123, 103, -91,
|
||||
29, -72, -122, 6, 67, -79, -24, -109, 95, -20, -125, -33, 8, -66, 19, 108, -104, 28, -81, 69, -112, 10, 112, -41, 85,
|
||||
-19, 109, -33, -63, 88, 56, -96, -13, 108, 21, 118, 53, -41, -91, 55, 99, -17, 45, 79, 18, -12, 82, -29, -97, 63,
|
||||
15, 127, -23, 7, 61, 120, -98, 74, -114, -35, 124, 30, -17, 71, 1, 121, 58, -58, -32, 100, 5, 49, -84, 79, 33,
|
||||
64, 109, -113, 76, -70, 0, 116, -42, 19, 97, -48, -88, -1, -121, 52, -80, 11, 46, -90, 2, -42, 78, -60, -92, -7,
|
||||
-75, 4, 112, -29, -94, 28, -65, -126, 124, -63, -115, 43, 113, -2, -118, 32, -83, 101, -59, -16, 10, -55, 106, -1, -96,
|
||||
-46, 90, -109, -63, -28, -98, 90, 48, -118, -61, 112, -41, -1, -63, -95, -7, -43, 39, -17, 63, -127, 57, -94, -19, 35,
|
||||
65, 95, 25, -52, 74, -114, 116, -30, 99, 25, -119, 40, 70, -106, 91, 41, -123, 21, 85, -45, 107, 3, -39, 36, 96,
|
||||
-52, -89, -34, 93, -51, 74, -113, 48, -84, 81, 44, -78, 61, 83, 19, -80, 33, 62, 98, 12, -15, -82, 30, 74, 16,
|
||||
-114, 56, 118, 13, 88, -85, 117, -100, -46, 94, -10, -66, 123, -118, -64, -32, -98, 121, -7, 28, -58, 64, -107, -61, 123,
|
||||
2, -46, 22, -23, -58, 73, -76, -1, -109, 53, -82, 73, -101, 11, -14, 75, 30, -77, 45, -1, -31, 16, 105, -124, -27,
|
||||
21, -107, -18, -56, 117, -5, -41, -120, 43, -55, 103, -4, -97, -19, 85, -77, -36, -110, 52, -32, 8, 85, 31, -88, 21,
|
||||
79, 44, -5, 107, 14, 62, -73, -36, 90, -92, -12, 34, 76, -24, -80, 107, 77, -83, 120, -18, 49, 114, -25, 15, 92,
|
||||
-16, 53, -76, 118, -123, -8, 111, -109, 82, -80, 67, -58, -7, 123, -51, 46, 107, -127, 64, -94, 85, -67, 127, -103, 50,
|
||||
-43, 116, -56, 37, -11, 93, 31, -62, 72, -122, -65, -21, 119, -48, -104, -36, -84, 55, -108, -13, 103, -126, 60, 18, 106,
|
||||
-50, -96, 14, 52, -126, -2, 37, -102, 16, -115, -65, 74, -91, -48, -117, 103, -35, 34, -67, 70, -46, 17, -59, 120, -103,
|
||||
11, 60, -99, 71, -74, -6, 25, -38, 42, -15, 28, -33, 74, 14, -128, 59, -88, 101, -121, 7, -91, 126, -13, 20, 77,
|
||||
46, -115, 59, -2, 104, 33, -47, 80, -60, 41, 6, -76, -32, -117, 55, 115, -45, 92, -65, 61, -49, 79, -42, 97, 27,
|
||||
-37, 42, 123, 29, 1, -96, 88, -18, 26, -95, 95, -9, 35, -26, 104, -70, 29, -29, 94, -97, 69, -70, 102, -115, 89,
|
||||
-93, -7, -78, 95, -18, 23, -37, 70, -66, 51, -43, -106, 102, -87, -38, 14, 91, -65, 17, -123, 118, 4, -95, 86, -46,
|
||||
125, 66, 12, -73, -2, -112, 24, -22, -110, 104, -79, 53, -13, -94, 88, -111, -29, -75, 65, -54, 52, -110, 125, -27, 42,
|
||||
-114, -71, 53, -111, -14, 79, -122, 19, -50, 120, 6, -86, 13, -60, 59, 113, -47, 43, -67, 124, -102, 15, 111, -6, 86,
|
||||
27, -48, 4, 114, -76, -22, -97, 65, -16, -71, 62, -23, 25, -107, -4, -91, 100, -22, 75, 42, -81, 127, -68, -3, 27,
|
||||
-124, 121, -61, -9, 64, 7, 88, -125, 115, -31, 9, -71, 80, -50, 71, 112, -47, 94, 9, -57, 109, -17, 47, -117, -16,
|
||||
55, 115, -32, 19, -117, 7, 87, -111, -10, 55, -80, -31, -114, -72, 70, -94, 58, -126, 74, 39, 126, -44, 96, 43, -113,
|
||||
107, -72, 92, 52, -55, 34, -105, -63, 98, -40, 49, 80, -47, 63, -31, 11, 48, 106, -87, -56, 37, -22, 23, -95, 94,
|
||||
50, -128, 23, -87, -10, 20, -81, 56, -103, 33, -88, 100, -68, 86, -44, -110, 42, -75, 77, -86, -28, 35, 83, -45, 92,
|
||||
39, 105, 12, -34, 107, -19, 24, -51, -4, -111, 24, -91, -53, 11, -40, 69, -15, -122, 114, -30, 88, 15, -11, -100, 13,
|
||||
-117, 117, -93, 83, -72, -104, -35, 27, 93, -100, 107, -73, 72, -52, -22, 2, 101, -39, 61, -121, 84, -26, 123, -48, 75,
|
||||
-32, 10, 37, -83, 65, -3, 110, -21, 28, 115, -60, -93, 22, -122, 1, -62, 62, -110, 32, -81, 90, -101, 105, -65, 87,
|
||||
0, 71, 122, -102, 26, -82, 43, 5, -91, 62, -128, 116, 56, -27, 28, -10, -57, 21, 111, -4, 76, -122, -16, 66, -45,
|
||||
1, 33, -105, 109, -85, 36, -102, 118, -56, 35, -69, 0, 22, -116, 61, -98, 125, -31, 93, -59, -102, 53, -48, -126, 71,
|
||||
-8, 120, -53, 76, -96, -15, 123, -48, 67, -37, 4, 61, -32, 41, -125, -20, -76, 55, -34, 109, -61, 78, -44, 26, -20,
|
||||
-56, -88, 96, -78, 54, 75, -118, -45, 61, -68, 126, 19, -77, 48, -120, 122, -12, 44, -56, 69, -28, 25, -8, 105, -109,
|
||||
82, -79, 104, -8, -50, 3, -125, 23, -10, 84, -70, 99, 9, -41, 39, -81, 51, -36, 97, 41, -89, 16, -119, 114, -69,
|
||||
21, -89, 118, -63, 34, 82, 3, -119, 70, -1, -107, 124, -75, 93, 36, 72, -41, 111, -107, -30, 93, 16, -97, 44, -28,
|
||||
-107, 102, -23, 78, -43, -77, 76, 9, -118, 85, -72, -97, 58, -16, 32, -40, 47, -73, 79, 40, 106, -79, 47, -116, -17,
|
||||
35, -92, 77, -110, 104, -14, -107, 5, -57, 89, -29, 46, -11, 75, -113, 85, -18, 61, -39, -98, 105, -15, -71, 49, -30,
|
||||
15, 65, -98, 2, -118, 18, -60, 6, 43, -69, -17, 105, -54, 8, 58, -71, 25, -102, 12, 99, -108, -37, 124, 3, 50,
|
||||
96, -44, -81, 112, -122, 9, 117, -105, -64, -29, 76, -46, 114, 66, -56, 126, -28, 19, -58, 73, 36, 117, -124, 30, -72,
|
||||
81, -86, -43, 44, -73, 15, -106, 100, 20, -54, 37, 116, -92, 92, -119, -12, -50, 117, -25, 77, -16, 82, -105, 113, 54,
|
||||
-127, 78, -91, 116, -11, 93, -65, 51, -21, 29, 58, -92, -66, -30, -126, 22, 80, 0, 65, -85, -20, 27, 72, -4, -100,
|
||||
14, -82, 1, -111, 27, -78, 90, -6, -99, -65, -30, 64, -9, 112, -105, 14, 125, -9, 104, -47, 73, -10, -74, 76, -110,
|
||||
-20, 23, -55, 55, 106, 30, -78, 57, -97, 125, -75, 21, -82, -39, 24, -22, 37, -42, -121, 62, -31, 119, -86, -58, 109,
|
||||
-7, 81, 39, 116, -22, -100, -62, -29, 94, 48, -49, -115, 122, 36, 96, -26, 46, 94, -11, 57, -119, 46, 110, 1, 84,
|
||||
-82, -51, 25, -24, 66, -71, -128, 29, -89, 41, -121, 127, -33, 59, -69, 70, -8, -88, -36, -111, 93, -52, 14, 62, -36,
|
||||
47, -7, 76, -95, 111, -68, 89, 23, -83, 7, -112, 70, 2, -119, -46, 17, -106, -57, 72, 37, 127, 18, -104, -69, 103,
|
||||
19, -37, -64, -127, 69, -94, -45, -71, 111, -21, -52, -87, 29, -110, 44, 95, -116, 52, -53, 89, -28, 79, 0, 105, -24,
|
||||
48, -95, -5, 95, -127, 122, 36, 84, 8, -24, 45, -125, -57, 92, -116, 119, -59, 6, 45, -112, -1, 68, -52, 87, 41,
|
||||
-44, 100, 43, 90, -83, 102, 2, -89, -6, -119, 57, -37, 66, -13, -83, 87, 58, 7, -36, 120, 13, 74, -101, 23, 99,
|
||||
67, -24, 123, -43, 10, -68, 109, -95, 5, 46, -74, -41, -100, 20, -73, 79, 30, -53, 18, -30, -66, -100, 53, -74, 109,
|
||||
-6, -85, 9, 68, -22, -107, 87, -51, 105, -26, -97, 115, -14, -79, 17, -98, -64, -19, 59, -43, 51, 109, -46, 90, -73,
|
||||
4, 114, -122, 47, -9, -102, 103, -79, 40, -117, -33, 52, -81, 6, -126, -66, 4, -103, 69, -18, 31, -38, 98, -106, 122,
|
||||
40, 85, -55, 113, -29, -106, 110, -80, 83, 2, 110, -43, 78, -101, 33, 115, -45, -90, 28, 61, -4, -92, 15, 56, -59,
|
||||
23, -127, 68, 126, -6, 30, 76, -124, 23, -67, -109, 40, -26, 64, -99, 31, -66, 98, -53, 35, -25, 72, -49, 93, -3,
|
||||
118, -41, 75, -10, 56, 97, 32, -79, 116, -124, 62, -76, -21, 13, -123, -10, 61, -115, 12, 63, -12, 33, -112, 46, -19,
|
||||
-122, 17, -16, 66, -120, 45, 97, -37, -75, 117, -35, 80, -114, 97, -29, 51, -70, -31, -114, 107, -76, -25, 123, -9, 78,
|
||||
-86, 12, 101, -51, -20, 71, 9, -88, 125, -120, 16, -92, 33, -68, -106, 39, -63, 115, -96, -55, -22, 85, -47, 18, -10,
|
||||
81, 38, -50, 96, -77, 28, -41, 94, -84, -49, 74, -58, 101, -80, 68, 121, -72, -40, -18, 8, -104, 71, 2, -124, 41,
|
||||
-64, -2, 32, -91, 104, 11, 83, 42, -43, 7, 82, -96, 60, -36, 113, -59, -127, 44, 119, -111, -38, 64, -20, 82, -41,
|
||||
109, -21, 83, 14, 90, -115, -25, 46, 12, -119, 51, -92, 75, -64, -109, 108, -85, 67, -24, 119, -94, -14, 50, 120, -97,
|
||||
20, -37, 38, 0, -99, 51, 95, 81, -76, 127, -61, 50, 87, -14, 105, -84, 68, -47, -4, -104, -49, -83, 63, -104, 39,
|
||||
-63, 18, -114, 48, -13, 82, 3, -78, 26, 101, -101, 30, -83, 43, -110, 64, -88, -53, -14, 22, 104, -87, 73, 127, -8,
|
||||
26, -34, 118, 8, -25, 22, -104, 48, -57, 80, 26, -125, -33, 1, 108, -117, 90, -62, -31, 6, -107
|
||||
};
|
||||
// Reference mask from: https://tellusim.com/download/noise/64x64_l64_s16.png
|
||||
// Made from: https://github.com/Tellusim/BlueNoise
|
||||
static final byte[] TELL_BLUE_NOISE = {
|
||||
18, 60, -13, 24, -93, -44, -107, 18, -34, 123, -120, 63, 115, 32, 91, -28, 118, -12, 97, -42, 77, -76, 30, -97, -39,
|
||||
87, -10, -57, 54, -3, -55, 118, 10, -35, 42, 124, -97, 57, -60, 1, -41, -103, -68, 97, -116, 44, -74, 60, -48, 87,
|
||||
-9, -117, 10, -73, 98, 40, -77, 55, -17, -86, 65, 36, 114, -48, -93, -39, -121, 98, -21, 119, 37, 84, -100, -2, 38,
|
||||
-26, -89, -10, -124, 13, -66, -115, 34, -89, -5, 121, -51, 98, 38, -118, 65, -88, 113, -124, 60, -18, -77, 101, -128, 2,
|
||||
-37, 95, -118, 86, 25, 58, -24, 69, -47, -9, 81, -127, 125, -77, 41, 111, -49, 53, -22, -108, 79, -61, 21, 126, -7,
|
||||
-128, -71, 71, 107, 36, 81, -56, 52, -80, -5, -55, 47, -77, 99, -57, 27, 76, -41, 106, 43, 78, -34, 107, 53, -123,
|
||||
21, -16, -70, 124, -29, 24, -48, 8, 96, -102, 78, -49, 25, 77, -70, 12, 47, -51, -80, 102, 13, -87, 36, 107, -85,
|
||||
27, -28, 10, -99, -24, 73, -101, 123, 15, -38, 97, -113, -51, 81, -34, 46, -20, 8, -82, -4, -100, 11, 74, -128, 112,
|
||||
-23, 81, 9, -112, 111, -79, 50, -103, -24, -81, 19, -99, -22, -65, 70, -101, 52, 5, -99, 84, 50, -78, -33, 39, -5,
|
||||
56, -93, -16, 111, -86, -27, 122, -2, -123, -53, 120, -106, -55, -16, 66, -59, 55, 105, -69, 29, -5, -57, 55, -94, 3,
|
||||
60, 31, -96, 17, 92, -109, -50, 124, 49, -30, 110, -58, 34, -81, 62, -105, -39, 48, -22, 14, -49, 87, 0, 126, -56,
|
||||
84, 31, 96, -8, 110, -47, 91, -67, -4, -116, 102, 68, -60, -117, 119, -64, 45, -117, 26, 69, -105, 45, 77, -15, 29,
|
||||
72, 8, 100, -98, 91, -113, -15, 82, -120, 108, -84, 90, -16, 117, -75, -20, 111, -57, -80, 65, 30, -120, -62, 69, -108,
|
||||
-9, 101, 11, -46, 30, 125, -85, 94, -119, 70, -83, 33, -126, 56, -4, -111, -42, -86, 20, -121, 35, -30, 116, 23, -21,
|
||||
-93, 11, 85, -40, 3, 75, -33, 100, -66, 8, -45, -84, 92, -76, -35, -122, 46, -65, -6, 31, -49, 49, 11, -27, 47,
|
||||
18, -125, -53, 67, -107, 51, -3, 102, -26, 86, 0, 97, 15, -72, 43, -35, -114, 70, -75, -9, 72, -47, 30, -7, 108,
|
||||
-42, 95, -25, -78, 115, 40, 68, -21, 80, -79, 57, -107, -53, 44, 127, -32, 30, -104, 107, -85, 20, -99, -13, 112, 67,
|
||||
24, -113, 1, 111, 22, -40, 121, 68, -79, 113, -97, -65, 69, -105, -42, 80, 28, -8, 87, -39, -124, 16, -93, 42, -38,
|
||||
-99, -23, 120, 66, -86, 93, -19, 103, -117, 10, -93, 62, -67, -103, 13, -89, 30, 76, 2, -64, -104, 123, -61, 14, 98,
|
||||
0, 71, -69, -125, 78, -79, 57, -14, 48, -45, 78, 36, -126, -73, -26, 105, 45, -61, 70, -83, 10, -116, -19, 57, 4,
|
||||
92, -35, 119, 1, -88, 110, -100, -60, 36, 122, 63, -58, 116, -73, 63, 24, -122, -51, 5, 38, -59, 27, -37, 50, 99,
|
||||
-38, 120, -21, 53, 86, -57, -118, -34, 101, 11, -32, 53, -103, -38, -85, 110, -6, 61, -50, 5, 100, -63, -120, 125, -90,
|
||||
-35, 57, 2, 73, -51, -90, -8, -109, 90, -31, 41, 95, -51, -126, 27, -81, 39, -59, 65, -34, 9, 101, -88, -24, -102,
|
||||
-13, -116, 4, 106, -62, 80, -11, 108, -109, 86, -89, 117, -65, -3, -123, 24, 77, -113, -35, 122, 7, 71, -89, 47, -128,
|
||||
103, -9, 64, 20, -45, -101, 17, 108, -112, -35, 15, 59, -25, 9, 97, -60, 116, -104, -3, 58, 126, 21, -54, 59, -98,
|
||||
-69, -2, 124, -24, 83, -111, 95, 20, -120, 54, -44, 25, 91, 13, 77, -39, 51, -102, -27, 39, -95, 56, -64, -15, 13,
|
||||
58, -101, 41, 91, -84, -58, -3, 31, -96, 41, -63, -5, 89, -45, 24, -90, 120, -118, 89, 52, -32, -67, 55, 82, -97,
|
||||
103, -80, 73, -111, 26, -81, 38, 92, -118, -32, -78, 99, -1, 115, 35, 74, -84, 61, -54, 3, -68, -18, 127, -78, 72,
|
||||
-114, -48, -83, 35, -64, 96, -6, 85, -72, 124, -37, 18, 70, -126, -29, 80, -47, -15, 12, 112, 56, -71, 75, -31, 108,
|
||||
-106, 33, -72, 84, -20, -52, 42, -23, -82, 115, 28, -90, -11, 35, -46, -3, 41, -50, -11, 82, -35, -62, 25, 82, 51,
|
||||
-106, -67, -22, -116, -38, 16, -104, 32, 109, 59, -86, 34, 4, -28, 104, 48, -15, 113, 18, -90, 37, -127, -12, 23, -112,
|
||||
91, -79, 110, 2, -87, 127, -111, 75, -34, -108, 102, -10, -121, 52, -40, 126, -10, -116, 57, 93, -70, 2, 70, -123, -4,
|
||||
97, -59, 119, -127, 87, -97, 122, 48, -123, 10, 117, -93, -2, -48, 18, 56, 86, 24, 110, -75, 78, -14, -123, -34, 84,
|
||||
-106, 91, -64, -3, -93, 84, -120, -17, 76, -43, 104, 60, -60, 49, -4, -42, 45, -61, 60, 21, -66, 50, -87, 7, -58,
|
||||
25, 94, -74, 18, -94, 67, 20, -80, 6, -111, 100, 26, -49, 49, -28, -108, 16, 68, -23, 14, -70, -26, 96, -72, 64,
|
||||
-27, 94, -128, 120, -33, -86, -58, -12, 40, -50, 121, -70, 48, -2, -52, 51, -126, 120, 29, -46, 61, 126, -65, 10, -94,
|
||||
-30, 87, -81, 113, -108, 13, 98, -118, -36, 106, -21, 31, 121, 71, -86, -26, 61, -11, 98, -62, -31, 118, 46, -59, -29,
|
||||
-88, 122, -100, 89, 62, -16, -90, -55, 95, 55, -102, 29, -7, -108, 33, -62, 58, 4, -104, 67, 104, -122, 86, -96, 28,
|
||||
5, 98, -96, 115, 6, -30, 60, -72, 8, -95, -27, -115, 49, 114, 21, -118, 8, -29, 69, -71, -17, 79, 7, -105, 81,
|
||||
-51, -127, -15, 45, -109, 115, -50, -125, 40, 74, -108, -14, 107, 9, 72, -14, 11, -78, -49, 30, 115, 42, -116, -5, 114,
|
||||
-61, 86, -40, 111, -5, -82, 80, -22, 31, -43, 52, -3, -33, 66, -115, -56, -21, 32, -78, 96, -102, -14, 83, 35, 96,
|
||||
68, -13, -75, -52, 63, 101, -62, 24, 122, -98, 31, -56, 53, -85, -5, 61, -61, 99, 16, -80, 34, 86, 4, -81, -42,
|
||||
81, -98, 44, -121, -64, 108, 43, 83, -104, -64, 4, 74, -42, -89, 64, 7, -80, 63, -115, 45, -58, 106, -82, 10, -103,
|
||||
118, -81, 89, -13, 44, 80, -111, 63, -50, 21, 106, -119, -55, -87, 14, -101, 82, 35, -12, -91, 49, -125, -23, 56, -74,
|
||||
109, -25, 91, 21, 114, -96, -2, -41, 64, -7, -103, -30, 114, 52, 16, -69, -37, 98, 34, -43, -115, -25, 14, 97, -29,
|
||||
-78, 103, 34, -32, -123, 100, 12, -43, 123, 13, -112, 55, -51, 73, 35, -45, 23, -94, 124, -77, 5, -27, 77, -84, -31,
|
||||
63, -1, 42, -45, 121, -28, -114, 110, -41, 88, -1, -59, 101, 1, -120, 36, -66, -112, -36, 49, 84, -120, 124, -61, 93,
|
||||
22, -55, -121, -11, 124, 60, -5, -92, 68, 0, 125, -87, 33, -125, 55, 7, -105, 127, 53, -17, -74, 36, -95, -35, 86,
|
||||
-21, 112, -75, -15, -127, 107, -27, 16, -44, 98, 45, -124, 122, 43, -67, 112, -108, 58, -71, 19, 66, -74, 27, -104, 72,
|
||||
26, -99, 75, -38, 121, -11, 73, 15, -90, -50, 38, -85, -18, 59, -87, 104, 72, -83, 27, -111, -55, 22, 105, -76, 58,
|
||||
-47, 68, -14, 91, -58, -9, -77, 15, -107, 62, 94, 1, 59, -66, 39, -117, 18, 84, 43, -61, 63, -113, 70, -93, -59,
|
||||
11, -44, 2, -99, -20, 88, -12, 102, -96, -8, 90, -21, 125, -69, -27, 51, -72, 22, -101, 44, -74, 95, -13, 109, 3,
|
||||
83, 32, -115, 5, -34, 35, -22, -63, 95, 71, -30, -128, -11, 27, -105, 100, -65, -99, 38, 68, 90, -52, 111, -30, -63,
|
||||
-125, 116, -90, -1, 97, -38, -87, -2, 93, -83, 38, -12, 116, 29, 85, -76, 101, 67, 24, -48, -128, 34, -41, 52, -121,
|
||||
-54, 11, 44, -116, 115, -18, 93, -46, 85, -23, -122, 55, -69, -110, -34, -75, 101, 69, -69, -106, 111, 50, -6, -95, 38,
|
||||
90, -60, 112, -31, 20, -6, 121, -39, -121, -23, 47, -84, 31, 83, -8, 29, -33, 76, -62, 55, 126, -111, -28, 10, 103,
|
||||
-74, -36, -110, -16, 37, -116, -33, -80, 127, -5, 78, -88, 118, 3, 65, -95, 102, -6, -83, 63, -128, 1, -87, 31, 126,
|
||||
-42, 19, 62, 119, 12, -52, -14, 40, 85, 7, -123, -47, 119, -71, 3, 69, -92, 51, -120, 73, -82, 2, 103, 23, -107,
|
||||
76, 5, -98, -46, 100, -110, 37, -103, 9, -71, 47, 78, -53, -123, 26, 88, 60, -86, 113, 6, 91, 42, -102, 47, -64,
|
||||
20, -25, -69, 93, -18, -61, 78, 18, -63, 33, 112, 66, -57, 3, -84, 92, -26, -94, 49, -127, 114, -93, -40, -78, 75,
|
||||
17, 56, -24, -113, 32, -15, -70, 104, -54, 40, 65, -93, -56, 113, -19, -54, 125, 60, -76, 4, 89, -18, 108, -36, 17,
|
||||
-91, 119, 57, -10, -62, 16, -46, 65, -59, -91, -23, 85, -44, 109, -117, 86, 38, -112, 21, 58, -105, -40, 99, -33, -94,
|
||||
-17, -114, 77, 36, -109, 4, -54, 100, -30, 17, 77, -3, 127, -25, -66, -101, 98, 73, -54, 124, 59, 11, -17, -113, -37,
|
||||
88, 7, 57, -81, 44, -115, 16, -27, 49, -53, -88, 43, -120, 69, -6, -40, -82, 99, -108, 123, -13, -124, -1, 74, 31,
|
||||
10, -90, -10, 66, -38, -86, 112, -73, -32, 124, 28, -113, 73, 0, 40, 104, -28, -61, 109, 67, 24, -79, 56, -102, -63,
|
||||
35, -112, 53, 106, 37, -4, -76, 4, -109, -39, -91, 75, 116, 25, -73, -25, -127, 100, -7, 80, -85, 96, -124, 121, -7,
|
||||
84, -44, 105, -103, 14, 40, -26, 48, -78, 79, 41, 108, -49, -112, -31, 94, 27, -74, 51, 14, -15, 47, 81, -92, -8,
|
||||
57, -70, 120, -52, -82, 10, 51, -90, -19, -118, 88, -3, 110, -19, 67, -45, 0, -90, -38, -126, 52, 102, 22, 89, 37,
|
||||
-67, -3, -106, 52, 121, 27, -34, -73, 34, -38, 0, 68, -77, 19, -100, 29, -62, 54, 96, -113, 77, -55, 17, -32, -95,
|
||||
-68, 22, 120, 46, -125, -54, 123, -109, 89, -53, -121, -2, -67, 107, -45, 22, -105, 54, 89, -124, 117, -46, 82, 44, -40,
|
||||
-68, 29, -123, 102, -74, 92, 23, 67, 121, -28, -64, -91, -13, -124, 62, -49, 95, -17, -91, -57, 59, 92, -111, 117, -60,
|
||||
26, -36, 62, -19, 116, -8, -87, -29, -68, -8, 94, -117, 105, 32, 71, -11, -85, 85, 3, 71, -25, 6, -76, 59, 118,
|
||||
24, 74, -20, -126, 69, -5, -40, 23, -68, -2, 32, -106, -7, 123, -98, 83, -31, 5, 44, -110, -16, -60, -94, -1, 79,
|
||||
48, 119, -37, 104, 20, -79, 74, 17, 107, -100, 8, -21, 49, -97, 106, -114, 91, -68, -128, 67, 28, 108, 63, 22, -84,
|
||||
54, 0, -50, -108, 102, -43, -17, -70, 40, -87, 108, 33, -26, -89, -42, -101, 37, 90, -75, 109, -99, 65, -27, 86, -85,
|
||||
99, -75, 39, -12, 54, -62, -93, 122, -39, 82, 39, 94, 26, -115, -49, 13, -80, -2, -110, 50, -44, -119, -24, 42, -51,
|
||||
79, -70, -14, 56, 5, -47, 37, 82, -39, -99, 4, -121, -40, 126, -26, -77, 115, 15, 53, -100, 34, 117, -105, 78, -50,
|
||||
-116, 87, 6, 101, 63, -57, -12, 28, -48, 5, 127, -112, 47, -44, 14, 68, -52, -114, 104, 12, 73, 25, -82, -6, -119,
|
||||
-67, -35, 111, 40, -104, 56, 86, -52, 127, -4, 92, 64, -77, 123, -123, 13, 99, -55, -92, 125, 9, -82, -5, 119, -53,
|
||||
89, 59, -105, 38, 84, -126, -21, -58, 93, -119, -37, 17, -20, 49, -4, -73, 46, -122, -1, -89, 100, -113, 72, -89, 32,
|
||||
-59, -4, 111, -126, -18, 114, 28, -76, -22, -121, -48, 87, 46, 117, 15, 64, -87, -11, 79, -68, -17, 33, -99, -67, 26,
|
||||
-101, -5, 31, -28, 59, -106, 40, 69, -19, -110, 96, 58, -69, 35, -14, -72, 7, -61, -6, 50, 75, -89, 7, 66, 88,
|
||||
-73, 105, -85, 71, 125, -24, -55, 110, 20, -34, 41, -7, 102, -20, 89, -97, 72, -67, 58, -92, -37, 81, 47, 106, 0,
|
||||
-100, -33, -60, -102, -20, 97, -56, 17, 119, -121, 99, 3, 78, -36, 106, -56, 88, -84, 113, -43, -1, -72, 88, -51, 21,
|
||||
-33, -118, 84, -92, 106, 47, 91, -90, -48, 12, 124, -31, -96, -53, 38, -113, 8, -46, -103, 23, 80, -108, 68, -79, 117,
|
||||
-70, -42, -117, 59, 6, -38, 34, -9, 92, 3, -108, -44, -86, 39, 112, 25, 65, 91, 36, -118, 58, -98, -30, 66, -39,
|
||||
-88, 43, -128, 16, 66, -113, 10, -62, 28, 102, -125, 3, 54, -95, 108, 50, 15, -45, -2, -122, -26, 31, 103, -115, -66,
|
||||
45, 20, -12, 95, -27, 113, 33, 65, -70, -9, 38, -40, 6, -122, 75, 49, 19, -65, 98, -83, 119, -116, -57, 41, 125,
|
||||
17, 72, -24, -64, -127, -3, -89, -54, 8, 110, -13, 41, -78, 26, 87, -63, 116, -19, -73, -12, 79, 54, -103, -18, 77,
|
||||
-66, 118, -20, -74, -11, -101, 118, 54, -59, 112, -98, -15, 76, 0, 101, -124, 70, -92, 54, -67, -127, -23, 103, -95, 121,
|
||||
-72, 92, 30, -27, -96, 122, -17, -109, 51, -25, 13, 67, -94, -15, -68, -116, 99, 13, 83, -32, 126, 57, -35, -81, 76,
|
||||
-60, 114, -114, 6, -32, 62, -95, 45, 122, -41, -77, 110, -54, 45, 11, -107, 32, 64, 88, -63, 24, -108, 80, 2, 48,
|
||||
-54, 34, -91, -44, 121, -57, 22, -38, 89, -3, 81, -54, 19, -28, 71, -98, -49, 105, 8, -56, 81, 27, -50, 75, -77,
|
||||
103, -36, 60, 108, 32, -39, -92, 42, -67, 16, -105, 94, 36, -125, 5, 53, -23, 100, -106, 20, 102, -58, 17, -107, 38,
|
||||
1, 64, -90, -27, 98, -43, -124, 12, -33, 97, -8, -42, -88, 72, -109, 113, -26, 50, -94, -8, 103, -108, 46, -83, 38,
|
||||
-103, 61, -120, 25, -10, 60, -79, 67, -128, 1, -86, 114, -103, -13, 32, -125, 9, -49, -99, 81, 1, 113, -112, 86, -9,
|
||||
-65, -19, 83, -47, -93, 73, -74, 50, -47, -6, -119, 83, -11, 99, -62, -120, 127, 29, -75, 77, -12, 122, -90, 57, -74,
|
||||
42, 126, -19, 12, -71, 73, 5, 83, 39, -71, -16, 126, 9, -42, 114, -1, 94, -53, 117, -112, 29, -18, 106, 43, -34,
|
||||
54, 15, 87, -69, 113, -82, 90, -7, 50, -60, -23, 66, -39, 45, 110, -100, 19, 120, 34, -54, 12, 125, -85, 89, 54,
|
||||
-36, -84, 54, -25, 84, -9, -53, 60, -101, 39, -69, 22, -110, 79, 6, -128, -52, 96, 37, -114, -52, -29, -119, 88, 26,
|
||||
-56, -117, 70, -84, -35, -96, 54, -75, 9, 91, -71, -44, -102, 80, -68, -119, -40, 42, -23, 19, 57, -66, -123, 103, -91,
|
||||
29, -72, -122, 6, 67, -79, -24, -109, 95, -20, -125, -33, 8, -66, 19, 108, -104, 28, -81, 69, -112, 10, 112, -41, 85,
|
||||
-19, 109, -33, -63, 88, 56, -96, -13, 108, 21, 118, 53, -41, -91, 55, 99, -17, 45, 79, 18, -12, 82, -29, -97, 63,
|
||||
15, 127, -23, 7, 61, 120, -98, 74, -114, -35, 124, 30, -17, 71, 1, 121, 58, -58, -32, 100, 5, 49, -84, 79, 33,
|
||||
64, 109, -113, 76, -70, 0, 116, -42, 19, 97, -48, -88, -1, -121, 52, -80, 11, 46, -90, 2, -42, 78, -60, -92, -7,
|
||||
-75, 4, 112, -29, -94, 28, -65, -126, 124, -63, -115, 43, 113, -2, -118, 32, -83, 101, -59, -16, 10, -55, 106, -1, -96,
|
||||
-46, 90, -109, -63, -28, -98, 90, 48, -118, -61, 112, -41, -1, -63, -95, -7, -43, 39, -17, 63, -127, 57, -94, -19, 35,
|
||||
65, 95, 25, -52, 74, -114, 116, -30, 99, 25, -119, 40, 70, -106, 91, 41, -123, 21, 85, -45, 107, 3, -39, 36, 96,
|
||||
-52, -89, -34, 93, -51, 74, -113, 48, -84, 81, 44, -78, 61, 83, 19, -80, 33, 62, 98, 12, -15, -82, 30, 74, 16,
|
||||
-114, 56, 118, 13, 88, -85, 117, -100, -46, 94, -10, -66, 123, -118, -64, -32, -98, 121, -7, 28, -58, 64, -107, -61, 123,
|
||||
2, -46, 22, -23, -58, 73, -76, -1, -109, 53, -82, 73, -101, 11, -14, 75, 30, -77, 45, -1, -31, 16, 105, -124, -27,
|
||||
21, -107, -18, -56, 117, -5, -41, -120, 43, -55, 103, -4, -97, -19, 85, -77, -36, -110, 52, -32, 8, 85, 31, -88, 21,
|
||||
79, 44, -5, 107, 14, 62, -73, -36, 90, -92, -12, 34, 76, -24, -80, 107, 77, -83, 120, -18, 49, 114, -25, 15, 92,
|
||||
-16, 53, -76, 118, -123, -8, 111, -109, 82, -80, 67, -58, -7, 123, -51, 46, 107, -127, 64, -94, 85, -67, 127, -103, 50,
|
||||
-43, 116, -56, 37, -11, 93, 31, -62, 72, -122, -65, -21, 119, -48, -104, -36, -84, 55, -108, -13, 103, -126, 60, 18, 106,
|
||||
-50, -96, 14, 52, -126, -2, 37, -102, 16, -115, -65, 74, -91, -48, -117, 103, -35, 34, -67, 70, -46, 17, -59, 120, -103,
|
||||
11, 60, -99, 71, -74, -6, 25, -38, 42, -15, 28, -33, 74, 14, -128, 59, -88, 101, -121, 7, -91, 126, -13, 20, 77,
|
||||
46, -115, 59, -2, 104, 33, -47, 80, -60, 41, 6, -76, -32, -117, 55, 115, -45, 92, -65, 61, -49, 79, -42, 97, 27,
|
||||
-37, 42, 123, 29, 1, -96, 88, -18, 26, -95, 95, -9, 35, -26, 104, -70, 29, -29, 94, -97, 69, -70, 102, -115, 89,
|
||||
-93, -7, -78, 95, -18, 23, -37, 70, -66, 51, -43, -106, 102, -87, -38, 14, 91, -65, 17, -123, 118, 4, -95, 86, -46,
|
||||
125, 66, 12, -73, -2, -112, 24, -22, -110, 104, -79, 53, -13, -94, 88, -111, -29, -75, 65, -54, 52, -110, 125, -27, 42,
|
||||
-114, -71, 53, -111, -14, 79, -122, 19, -50, 120, 6, -86, 13, -60, 59, 113, -47, 43, -67, 124, -102, 15, 111, -6, 86,
|
||||
27, -48, 4, 114, -76, -22, -97, 65, -16, -71, 62, -23, 25, -107, -4, -91, 100, -22, 75, 42, -81, 127, -68, -3, 27,
|
||||
-124, 121, -61, -9, 64, 7, 88, -125, 115, -31, 9, -71, 80, -50, 71, 112, -47, 94, 9, -57, 109, -17, 47, -117, -16,
|
||||
55, 115, -32, 19, -117, 7, 87, -111, -10, 55, -80, -31, -114, -72, 70, -94, 58, -126, 74, 39, 126, -44, 96, 43, -113,
|
||||
107, -72, 92, 52, -55, 34, -105, -63, 98, -40, 49, 80, -47, 63, -31, 11, 48, 106, -87, -56, 37, -22, 23, -95, 94,
|
||||
50, -128, 23, -87, -10, 20, -81, 56, -103, 33, -88, 100, -68, 86, -44, -110, 42, -75, 77, -86, -28, 35, 83, -45, 92,
|
||||
39, 105, 12, -34, 107, -19, 24, -51, -4, -111, 24, -91, -53, 11, -40, 69, -15, -122, 114, -30, 88, 15, -11, -100, 13,
|
||||
-117, 117, -93, 83, -72, -104, -35, 27, 93, -100, 107, -73, 72, -52, -22, 2, 101, -39, 61, -121, 84, -26, 123, -48, 75,
|
||||
-32, 10, 37, -83, 65, -3, 110, -21, 28, 115, -60, -93, 22, -122, 1, -62, 62, -110, 32, -81, 90, -101, 105, -65, 87,
|
||||
0, 71, 122, -102, 26, -82, 43, 5, -91, 62, -128, 116, 56, -27, 28, -10, -57, 21, 111, -4, 76, -122, -16, 66, -45,
|
||||
1, 33, -105, 109, -85, 36, -102, 118, -56, 35, -69, 0, 22, -116, 61, -98, 125, -31, 93, -59, -102, 53, -48, -126, 71,
|
||||
-8, 120, -53, 76, -96, -15, 123, -48, 67, -37, 4, 61, -32, 41, -125, -20, -76, 55, -34, 109, -61, 78, -44, 26, -20,
|
||||
-56, -88, 96, -78, 54, 75, -118, -45, 61, -68, 126, 19, -77, 48, -120, 122, -12, 44, -56, 69, -28, 25, -8, 105, -109,
|
||||
82, -79, 104, -8, -50, 3, -125, 23, -10, 84, -70, 99, 9, -41, 39, -81, 51, -36, 97, 41, -89, 16, -119, 114, -69,
|
||||
21, -89, 118, -63, 34, 82, 3, -119, 70, -1, -107, 124, -75, 93, 36, 72, -41, 111, -107, -30, 93, 16, -97, 44, -28,
|
||||
-107, 102, -23, 78, -43, -77, 76, 9, -118, 85, -72, -97, 58, -16, 32, -40, 47, -73, 79, 40, 106, -79, 47, -116, -17,
|
||||
35, -92, 77, -110, 104, -14, -107, 5, -57, 89, -29, 46, -11, 75, -113, 85, -18, 61, -39, -98, 105, -15, -71, 49, -30,
|
||||
15, 65, -98, 2, -118, 18, -60, 6, 43, -69, -17, 105, -54, 8, 58, -71, 25, -102, 12, 99, -108, -37, 124, 3, 50,
|
||||
96, -44, -81, 112, -122, 9, 117, -105, -64, -29, 76, -46, 114, 66, -56, 126, -28, 19, -58, 73, 36, 117, -124, 30, -72,
|
||||
81, -86, -43, 44, -73, 15, -106, 100, 20, -54, 37, 116, -92, 92, -119, -12, -50, 117, -25, 77, -16, 82, -105, 113, 54,
|
||||
-127, 78, -91, 116, -11, 93, -65, 51, -21, 29, 58, -92, -66, -30, -126, 22, 80, 0, 65, -85, -20, 27, 72, -4, -100,
|
||||
14, -82, 1, -111, 27, -78, 90, -6, -99, -65, -30, 64, -9, 112, -105, 14, 125, -9, 104, -47, 73, -10, -74, 76, -110,
|
||||
-20, 23, -55, 55, 106, 30, -78, 57, -97, 125, -75, 21, -82, -39, 24, -22, 37, -42, -121, 62, -31, 119, -86, -58, 109,
|
||||
-7, 81, 39, 116, -22, -100, -62, -29, 94, 48, -49, -115, 122, 36, 96, -26, 46, 94, -11, 57, -119, 46, 110, 1, 84,
|
||||
-82, -51, 25, -24, 66, -71, -128, 29, -89, 41, -121, 127, -33, 59, -69, 70, -8, -88, -36, -111, 93, -52, 14, 62, -36,
|
||||
47, -7, 76, -95, 111, -68, 89, 23, -83, 7, -112, 70, 2, -119, -46, 17, -106, -57, 72, 37, 127, 18, -104, -69, 103,
|
||||
19, -37, -64, -127, 69, -94, -45, -71, 111, -21, -52, -87, 29, -110, 44, 95, -116, 52, -53, 89, -28, 79, 0, 105, -24,
|
||||
48, -95, -5, 95, -127, 122, 36, 84, 8, -24, 45, -125, -57, 92, -116, 119, -59, 6, 45, -112, -1, 68, -52, 87, 41,
|
||||
-44, 100, 43, 90, -83, 102, 2, -89, -6, -119, 57, -37, 66, -13, -83, 87, 58, 7, -36, 120, 13, 74, -101, 23, 99,
|
||||
67, -24, 123, -43, 10, -68, 109, -95, 5, 46, -74, -41, -100, 20, -73, 79, 30, -53, 18, -30, -66, -100, 53, -74, 109,
|
||||
-6, -85, 9, 68, -22, -107, 87, -51, 105, -26, -97, 115, -14, -79, 17, -98, -64, -19, 59, -43, 51, 109, -46, 90, -73,
|
||||
4, 114, -122, 47, -9, -102, 103, -79, 40, -117, -33, 52, -81, 6, -126, -66, 4, -103, 69, -18, 31, -38, 98, -106, 122,
|
||||
40, 85, -55, 113, -29, -106, 110, -80, 83, 2, 110, -43, 78, -101, 33, 115, -45, -90, 28, 61, -4, -92, 15, 56, -59,
|
||||
23, -127, 68, 126, -6, 30, 76, -124, 23, -67, -109, 40, -26, 64, -99, 31, -66, 98, -53, 35, -25, 72, -49, 93, -3,
|
||||
118, -41, 75, -10, 56, 97, 32, -79, 116, -124, 62, -76, -21, 13, -123, -10, 61, -115, 12, 63, -12, 33, -112, 46, -19,
|
||||
-122, 17, -16, 66, -120, 45, 97, -37, -75, 117, -35, 80, -114, 97, -29, 51, -70, -31, -114, 107, -76, -25, 123, -9, 78,
|
||||
-86, 12, 101, -51, -20, 71, 9, -88, 125, -120, 16, -92, 33, -68, -106, 39, -63, 115, -96, -55, -22, 85, -47, 18, -10,
|
||||
81, 38, -50, 96, -77, 28, -41, 94, -84, -49, 74, -58, 101, -80, 68, 121, -72, -40, -18, 8, -104, 71, 2, -124, 41,
|
||||
-64, -2, 32, -91, 104, 11, 83, 42, -43, 7, 82, -96, 60, -36, 113, -59, -127, 44, 119, -111, -38, 64, -20, 82, -41,
|
||||
109, -21, 83, 14, 90, -115, -25, 46, 12, -119, 51, -92, 75, -64, -109, 108, -85, 67, -24, 119, -94, -14, 50, 120, -97,
|
||||
20, -37, 38, 0, -99, 51, 95, 81, -76, 127, -61, 50, 87, -14, 105, -84, 68, -47, -4, -104, -49, -83, 63, -104, 39,
|
||||
-63, 18, -114, 48, -13, 82, 3, -78, 26, 101, -101, 30, -83, 43, -110, 64, -88, -53, -14, 22, 104, -87, 73, 127, -8,
|
||||
26, -34, 118, 8, -25, 22, -104, 48, -57, 80, 26, -125, -33, 1, 108, -117, 90, -62, -31, 6, -107
|
||||
};
|
||||
|
||||
public static int diffuse(final int pixel, final int qPixel, final float weight, final float strength, final int x, final int y) {
|
||||
int r_pix = Color.red(pixel);
|
||||
int g_pix = Color.green(pixel);
|
||||
int b_pix = Color.blue(pixel);
|
||||
int a_pix = Color.alpha(pixel);
|
||||
public static int diffuse(final int pixel, final int qPixel, final float weight, final float strength, final int x, final int y)
|
||||
{
|
||||
int r_pix = Color.red(pixel);
|
||||
int g_pix = Color.green(pixel);
|
||||
int b_pix = Color.blue(pixel);
|
||||
int a_pix = Color.alpha(pixel);
|
||||
|
||||
float adj = (TELL_BLUE_NOISE[(x & 63) | (y & 63) << 6] + 0.5f) / 127.5f;
|
||||
adj += ((x + y & 1) - 0.5f) * strength / 8f;
|
||||
adj *= weight;
|
||||
float adj = (TELL_BLUE_NOISE[(x & 63) | (y & 63) << 6] + 0.5f) / 127.5f;
|
||||
adj += ((x + y & 1) - 0.5f) * strength / 8f;
|
||||
adj *= weight;
|
||||
|
||||
r_pix = (int) Math.min(0xFF, Math.max(r_pix + (adj * (r_pix - Color.red(qPixel))), 0.0));
|
||||
g_pix = (int) Math.min(0xFF, Math.max(g_pix + (adj * (g_pix - Color.green(qPixel))), 0.0));
|
||||
b_pix = (int) Math.min(0xFF, Math.max(b_pix + (adj * (b_pix - Color.blue(qPixel))), 0.0));
|
||||
a_pix = (int) Math.min(0xFF, Math.max(a_pix + (adj * (a_pix - Color.alpha(qPixel))), 0.0));
|
||||
r_pix = (int) Math.min(0xFF, Math.max(r_pix + (adj * (r_pix - Color.red(qPixel))), 0.0));
|
||||
g_pix = (int) Math.min(0xFF, Math.max(g_pix + (adj * (g_pix - Color.green(qPixel))), 0.0));
|
||||
b_pix = (int) Math.min(0xFF, Math.max(b_pix + (adj * (b_pix - Color.blue(qPixel))), 0.0));
|
||||
a_pix = (int) Math.min(0xFF, Math.max(a_pix + (adj * (a_pix - Color.alpha(qPixel))), 0.0));
|
||||
|
||||
return Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
}
|
||||
|
||||
return Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
static int[] processImagePixels(final Integer[] palette, final int[] qPixels) {
|
||||
int[] qPixel32s = new int[qPixels.length];
|
||||
for (var i = 0; i < qPixels.length; ++i)
|
||||
qPixel32s[i] = palette[qPixels[i]];
|
||||
|
||||
return qPixel32s;
|
||||
}
|
||||
|
||||
public static int[] dither(final int width, final int height, final int[] pixels, final int[] palette, final Ditherable ditherable, final int[] qPixels, final float weight) {
|
||||
final float strength = 1 / 3f;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
final int bidx = x + y * width;
|
||||
int pixel = pixels[bidx];
|
||||
int qPixel = palette[qPixels[bidx]];
|
||||
public static int[] dither(final int width, final int height, final int[] pixels, final Integer[] palette, final Ditherable ditherable, final int[] qPixels, final float weight)
|
||||
{
|
||||
final float strength = 1 / 3f;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
final int bidx = x + y * width;
|
||||
int pixel = pixels[bidx];
|
||||
int qPixel = palette[qPixels[bidx]];
|
||||
|
||||
int c1 = diffuse(pixel, qPixel, weight, strength, x, y);
|
||||
qPixels[bidx] = palette[ditherable.nearestColorIndex(palette, c1, bidx)];
|
||||
}
|
||||
}
|
||||
int c1 = diffuse(pixel, qPixel, weight, strength, x, y);
|
||||
qPixels[bidx] = palette[ditherable.nearestColorIndex(palette, c1, bidx)];
|
||||
}
|
||||
}
|
||||
|
||||
return qPixels;
|
||||
}
|
||||
return qPixels;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,229 +1,239 @@
|
||||
package com.android.nQuant;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class CIELABConvertor {
|
||||
private final static char BYTE_MAX = -Byte.MIN_VALUE + Byte.MAX_VALUE;
|
||||
private static final double XYZ_WHITE_REFERENCE_Y = 100;
|
||||
private final static char BYTE_MAX = -Byte.MIN_VALUE + Byte.MAX_VALUE;
|
||||
private static final double XYZ_WHITE_REFERENCE_Y = 100;
|
||||
|
||||
static class MutableDouble extends Number {
|
||||
static class MutableDouble extends Number {
|
||||
|
||||
private static final long serialVersionUID = -8826262264116498065L;
|
||||
private double value;
|
||||
private static final long serialVersionUID = -8826262264116498065L;
|
||||
private double value;
|
||||
|
||||
public MutableDouble(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
public MutableDouble(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public MutableDouble() {
|
||||
this(0.0);
|
||||
}
|
||||
|
||||
public MutableDouble() {
|
||||
this(0.0);
|
||||
}
|
||||
public void setValue(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
@Override
|
||||
public int intValue() {
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return (int) value;
|
||||
}
|
||||
@Override
|
||||
public long longValue() {
|
||||
return (long) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long longValue() {
|
||||
return (long) value;
|
||||
}
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return (float) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float floatValue() {
|
||||
return (float) value;
|
||||
}
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Lab {
|
||||
float alpha = BYTE_MAX;
|
||||
float A = 0f;
|
||||
float B = 0f;
|
||||
float L = 0f;
|
||||
}
|
||||
|
||||
static Lab RGB2LAB(final int c1)
|
||||
{
|
||||
double[] labs = new double[3];
|
||||
ColorUtils.colorToLAB(c1, labs);
|
||||
|
||||
@Override
|
||||
public double doubleValue() {
|
||||
return value;
|
||||
}
|
||||
Lab lab = new Lab();
|
||||
lab.alpha = Color.alpha(c1);
|
||||
lab.L = (float) labs[0];
|
||||
lab.A = (float) labs[1];
|
||||
lab.B = (float) labs[2];
|
||||
return lab;
|
||||
}
|
||||
|
||||
}
|
||||
protected static double gammaToLinear(int channel)
|
||||
{
|
||||
final double c = channel / 255.0;
|
||||
return c < 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
|
||||
static class Lab {
|
||||
float alpha = BYTE_MAX;
|
||||
float A = 0f;
|
||||
float B = 0f;
|
||||
float L = 0f;
|
||||
}
|
||||
static int LAB2RGB(final Lab lab){
|
||||
int color = ColorUtils.LABToColor(lab.L, lab.A, lab.B);
|
||||
return ColorUtils.setAlphaComponent(color, (int) lab.alpha);
|
||||
}
|
||||
|
||||
static Lab RGB2LAB(final int c1) {
|
||||
double[] labs = new double[3];
|
||||
ColorUtils.colorToLAB(c1, labs);
|
||||
/*******************************************************************************
|
||||
* Conversions.
|
||||
******************************************************************************/
|
||||
|
||||
Lab lab = new Lab();
|
||||
lab.alpha = Color.alpha(c1);
|
||||
lab.L = (float) labs[0];
|
||||
lab.A = (float) labs[1];
|
||||
lab.B = (float) labs[2];
|
||||
return lab;
|
||||
}
|
||||
private static final float deg2Rad(final double deg)
|
||||
{
|
||||
return (float) (deg * (Math.PI / 180.0));
|
||||
}
|
||||
|
||||
protected static double gammaToLinear(int channel) {
|
||||
final double c = channel / 255.0;
|
||||
return c < 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
||||
}
|
||||
static float L_prime_div_k_L_S_L(final Lab lab1, final Lab lab2)
|
||||
{
|
||||
final float k_L = 1.0f;
|
||||
float deltaLPrime = lab2.L - lab1.L;
|
||||
float barLPrime = (lab1.L + lab2.L) / 2f;
|
||||
float S_L = (float)(1 + ((0.015f * Math.pow(barLPrime - 50f, 2f)) / Math.sqrt(20 + Math.pow(barLPrime - 50f, 2f))));
|
||||
return deltaLPrime / (k_L * S_L);
|
||||
}
|
||||
|
||||
static int LAB2RGB(final Lab lab) {
|
||||
int color = ColorUtils.LABToColor(lab.L, lab.A, lab.B);
|
||||
return ColorUtils.setAlphaComponent(color, (int) lab.alpha);
|
||||
}
|
||||
static float C_prime_div_k_L_S_L(final Lab lab1, final Lab lab2, MutableDouble a1Prime, MutableDouble a2Prime, MutableDouble CPrime1, MutableDouble CPrime2)
|
||||
{
|
||||
final float k_C = 1f;
|
||||
final float pow25To7 = 6103515625f; /* pow(25, 7) */
|
||||
float C1 = (float)(Math.sqrt((lab1.A * lab1.A) + (lab1.B * lab1.B)));
|
||||
float C2 = (float)(Math.sqrt((lab2.A * lab2.A) + (lab2.B * lab2.B)));
|
||||
float barC = (C1 + C2) / 2f;
|
||||
float G = (float)(0.5f * (1 - Math.sqrt(Math.pow(barC, 7) / (Math.pow(barC, 7) + pow25To7))));
|
||||
a1Prime.setValue((1.0 + G) * lab1.A);
|
||||
a2Prime.setValue((1.0 + G) * lab2.A);
|
||||
|
||||
/*******************************************************************************
|
||||
* Conversions.
|
||||
******************************************************************************/
|
||||
CPrime1.setValue(Math.sqrt((a1Prime.doubleValue() * a1Prime.doubleValue()) + (lab1.B * lab1.B)));
|
||||
CPrime2.setValue(Math.sqrt((a2Prime.doubleValue() * a2Prime.doubleValue()) + (lab2.B * lab2.B)));
|
||||
float deltaCPrime = CPrime2.floatValue() - CPrime1.floatValue();
|
||||
float barCPrime = (CPrime1.floatValue() + CPrime2.floatValue()) / 2f;
|
||||
|
||||
float S_C = 1 + (0.045f * barCPrime);
|
||||
return deltaCPrime / (k_C * S_C);
|
||||
}
|
||||
|
||||
private static final float deg2Rad(final double deg) {
|
||||
return (float) (deg * (Math.PI / 180.0));
|
||||
}
|
||||
static float H_prime_div_k_L_S_L(final Lab lab1, final Lab lab2, final Number a1Prime, final Number a2Prime, final Number CPrime1, final Number CPrime2, MutableDouble barCPrime, MutableDouble barhPrime)
|
||||
{
|
||||
final float k_H = 1f;
|
||||
final float deg360InRad = deg2Rad(360f);
|
||||
final float deg180InRad = deg2Rad(180f);
|
||||
double CPrimeProduct = CPrime1.doubleValue() * CPrime2.doubleValue();
|
||||
double hPrime1;
|
||||
if (BigDecimal.ZERO.equals(new BigDecimal(lab1.B)) && BigDecimal.ZERO.equals(new BigDecimal(a1Prime.doubleValue())))
|
||||
hPrime1 = 0.0;
|
||||
else {
|
||||
hPrime1 = Math.atan2(lab1.B, a1Prime.doubleValue());
|
||||
/*
|
||||
* This must be converted to a hue angle in degrees between 0
|
||||
* and 360 by addition of 2π to negative hue angles.
|
||||
*/
|
||||
if (hPrime1 < 0)
|
||||
hPrime1 += deg360InRad;
|
||||
}
|
||||
double hPrime2;
|
||||
if (BigDecimal.ZERO.equals(new BigDecimal(lab2.B)) && BigDecimal.ZERO.equals(new BigDecimal(a2Prime.doubleValue())))
|
||||
hPrime2 = 0.0;
|
||||
else {
|
||||
hPrime2 = Math.atan2(lab2.B, a2Prime.doubleValue());
|
||||
/*
|
||||
* This must be converted to a hue angle in degrees between 0
|
||||
* and 360 by addition of 2π to negative hue angles.
|
||||
*/
|
||||
if (hPrime2 < 0)
|
||||
hPrime2 += deg360InRad;
|
||||
}
|
||||
double deltahPrime;
|
||||
if (BigDecimal.ZERO.equals(new BigDecimal(CPrimeProduct)))
|
||||
deltahPrime = 0;
|
||||
else {
|
||||
/* Avoid the Math.abs() call */
|
||||
deltahPrime = hPrime2 - hPrime1;
|
||||
if (deltahPrime < -deg180InRad)
|
||||
deltahPrime += deg360InRad;
|
||||
else if (deltahPrime > deg180InRad)
|
||||
deltahPrime -= deg360InRad;
|
||||
}
|
||||
|
||||
static float L_prime_div_k_L_S_L(final Lab lab1, final Lab lab2) {
|
||||
final float k_L = 1.0f;
|
||||
float deltaLPrime = lab2.L - lab1.L;
|
||||
float barLPrime = (lab1.L + lab2.L) / 2f;
|
||||
float S_L = (float) (1 + ((0.015f * Math.pow(barLPrime - 50f, 2f)) / Math.sqrt(20 + Math.pow(barLPrime - 50f, 2f))));
|
||||
return deltaLPrime / (k_L * S_L);
|
||||
}
|
||||
double deltaHPrime = 2.0 * Math.sqrt(CPrimeProduct) * Math.sin(deltahPrime / 2.0);
|
||||
double hPrimeSum = hPrime1 + hPrime2;
|
||||
if (BigDecimal.ZERO.equals(new BigDecimal(CPrime1.doubleValue() * CPrime2.doubleValue()))) {
|
||||
barhPrime.setValue(hPrimeSum);
|
||||
}
|
||||
else {
|
||||
if (Math.abs(hPrime1 - hPrime2) <= deg180InRad)
|
||||
barhPrime.setValue(hPrimeSum / 2.0);
|
||||
else {
|
||||
if (hPrimeSum < deg360InRad)
|
||||
barhPrime.setValue((hPrimeSum + deg360InRad) / 2.0);
|
||||
else
|
||||
barhPrime.setValue((hPrimeSum - deg360InRad) / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
static float C_prime_div_k_L_S_L(final Lab lab1, final Lab lab2, MutableDouble a1Prime, MutableDouble a2Prime, MutableDouble CPrime1, MutableDouble CPrime2) {
|
||||
final float k_C = 1f;
|
||||
final float pow25To7 = 6103515625f; /* pow(25, 7) */
|
||||
float C1 = (float) (Math.sqrt((lab1.A * lab1.A) + (lab1.B * lab1.B)));
|
||||
float C2 = (float) (Math.sqrt((lab2.A * lab2.A) + (lab2.B * lab2.B)));
|
||||
float barC = (C1 + C2) / 2f;
|
||||
float G = (float) (0.5f * (1 - Math.sqrt(Math.pow(barC, 7) / (Math.pow(barC, 7) + pow25To7))));
|
||||
a1Prime.setValue((1.0 + G) * lab1.A);
|
||||
a2Prime.setValue((1.0 + G) * lab2.A);
|
||||
barCPrime.setValue((CPrime1.doubleValue() + CPrime2.doubleValue()) / 2.0);
|
||||
double T = 1.0 - (0.17 * Math.cos(barhPrime.doubleValue() - deg2Rad(30f))) +
|
||||
(0.24 * Math.cos(2.0 * barhPrime.doubleValue())) +
|
||||
(0.32 * Math.cos((3.0 * barhPrime.doubleValue()) + deg2Rad(6f))) -
|
||||
(0.20 * Math.cos((4.0 * barhPrime.doubleValue()) - deg2Rad(63f)));
|
||||
double S_H = 1 + (0.015f * barCPrime.doubleValue() * T);
|
||||
return (float) (deltaHPrime / (k_H * S_H));
|
||||
}
|
||||
|
||||
CPrime1.setValue(Math.sqrt((a1Prime.doubleValue() * a1Prime.doubleValue()) + (lab1.B * lab1.B)));
|
||||
CPrime2.setValue(Math.sqrt((a2Prime.doubleValue() * a2Prime.doubleValue()) + (lab2.B * lab2.B)));
|
||||
float deltaCPrime = CPrime2.floatValue() - CPrime1.floatValue();
|
||||
float barCPrime = (CPrime1.floatValue() + CPrime2.floatValue()) / 2f;
|
||||
static float R_T(final Number barCPrime, final Number barhPrime, final float C_prime_div_k_L_S_L, final float H_prime_div_k_L_S_L)
|
||||
{
|
||||
final double pow25To7 = 6103515625.0; /* Math.pow(25, 7) */
|
||||
double deltaTheta = deg2Rad(30f) * Math.exp(-Math.pow((barhPrime.doubleValue() - deg2Rad(275f)) / deg2Rad(25f), 2.0));
|
||||
double R_C = 2.0 * Math.sqrt(Math.pow(barCPrime.doubleValue(), 7.0) / (Math.pow(barCPrime.doubleValue(), 7.0) + pow25To7));
|
||||
double R_T = (-Math.sin(2.0 * deltaTheta)) * R_C;
|
||||
return (float) (R_T * C_prime_div_k_L_S_L * H_prime_div_k_L_S_L);
|
||||
}
|
||||
|
||||
float S_C = 1 + (0.045f * barCPrime);
|
||||
return deltaCPrime / (k_C * S_C);
|
||||
}
|
||||
/* From the paper "The CIEDE2000 Color-Difference Formula: Implementation Notes, */
|
||||
/* Supplementary Test Data, and Mathematical Observations", by */
|
||||
/* Gaurav Sharma, Wencheng Wu and Edul N. Dalal, */
|
||||
/* Color Res. Appl., vol. 30, no. 1, pp. 21-30, Feb. 2005. */
|
||||
/* Return the CIEDE2000 Delta E color difference measure squared, for two Lab values */
|
||||
static float CIEDE2000(final Lab lab1, final Lab lab2)
|
||||
{
|
||||
float deltaL_prime_div_k_L_S_L = L_prime_div_k_L_S_L(lab1, lab2);
|
||||
MutableDouble a1Prime = new MutableDouble(), a2Prime = new MutableDouble(), CPrime1 = new MutableDouble(), CPrime2 = new MutableDouble();
|
||||
float deltaC_prime_div_k_L_S_L = C_prime_div_k_L_S_L(lab1, lab2, a1Prime, a2Prime, CPrime1, CPrime2);
|
||||
MutableDouble barCPrime = new MutableDouble(), barhPrime = new MutableDouble();
|
||||
float deltaH_prime_div_k_L_S_L = H_prime_div_k_L_S_L(lab1, lab2, a1Prime, a2Prime, CPrime1, CPrime2, barCPrime, barhPrime);
|
||||
float deltaR_T = R_T(barCPrime, barhPrime, deltaC_prime_div_k_L_S_L, deltaH_prime_div_k_L_S_L);
|
||||
return (float) (Math.pow(deltaL_prime_div_k_L_S_L, 2.0) +
|
||||
Math.pow(deltaC_prime_div_k_L_S_L, 2.0) +
|
||||
Math.pow(deltaH_prime_div_k_L_S_L, 2.0) +
|
||||
deltaR_T);
|
||||
}
|
||||
|
||||
static float H_prime_div_k_L_S_L(final Lab lab1, final Lab lab2, final Number a1Prime, final Number a2Prime, final Number CPrime1, final Number CPrime2, MutableDouble barCPrime, MutableDouble barhPrime) {
|
||||
final float k_H = 1f;
|
||||
final float deg360InRad = deg2Rad(360f);
|
||||
final float deg180InRad = deg2Rad(180f);
|
||||
double CPrimeProduct = CPrime1.doubleValue() * CPrime2.doubleValue();
|
||||
double hPrime1;
|
||||
if (BigDecimal.ZERO.equals(new BigDecimal(lab1.B)) && BigDecimal.ZERO.equals(new BigDecimal(a1Prime.doubleValue())))
|
||||
hPrime1 = 0.0;
|
||||
else {
|
||||
hPrime1 = Math.atan2(lab1.B, a1Prime.doubleValue());
|
||||
/*
|
||||
* This must be converted to a hue angle in degrees between 0
|
||||
* and 360 by addition of 2π to negative hue angles.
|
||||
*/
|
||||
if (hPrime1 < 0)
|
||||
hPrime1 += deg360InRad;
|
||||
}
|
||||
double hPrime2;
|
||||
if (BigDecimal.ZERO.equals(new BigDecimal(lab2.B)) && BigDecimal.ZERO.equals(new BigDecimal(a2Prime.doubleValue())))
|
||||
hPrime2 = 0.0;
|
||||
else {
|
||||
hPrime2 = Math.atan2(lab2.B, a2Prime.doubleValue());
|
||||
/*
|
||||
* This must be converted to a hue angle in degrees between 0
|
||||
* and 360 by addition of 2π to negative hue angles.
|
||||
*/
|
||||
if (hPrime2 < 0)
|
||||
hPrime2 += deg360InRad;
|
||||
}
|
||||
double deltahPrime;
|
||||
if (BigDecimal.ZERO.equals(new BigDecimal(CPrimeProduct)))
|
||||
deltahPrime = 0;
|
||||
else {
|
||||
/* Avoid the Math.abs() call */
|
||||
deltahPrime = hPrime2 - hPrime1;
|
||||
if (deltahPrime < -deg180InRad)
|
||||
deltahPrime += deg360InRad;
|
||||
else if (deltahPrime > deg180InRad)
|
||||
deltahPrime -= deg360InRad;
|
||||
}
|
||||
static double Y_Diff(final int c1, final int c2)
|
||||
{
|
||||
java.util.function.Function<Integer, Double> color2Y = c -> {
|
||||
double sr = gammaToLinear(Color.red(c));
|
||||
double sg = gammaToLinear(Color.green(c));
|
||||
double sb = gammaToLinear(Color.blue(c));
|
||||
return sr * 0.2126 + sg * 0.7152 + sb * 0.0722;
|
||||
};
|
||||
|
||||
double y = color2Y.apply(c1);
|
||||
double y2 = color2Y.apply(c2);
|
||||
return Math.abs(y2 - y) * XYZ_WHITE_REFERENCE_Y;
|
||||
}
|
||||
|
||||
double deltaHPrime = 2.0 * Math.sqrt(CPrimeProduct) * Math.sin(deltahPrime / 2.0);
|
||||
double hPrimeSum = hPrime1 + hPrime2;
|
||||
if (BigDecimal.ZERO.equals(new BigDecimal(CPrime1.doubleValue() * CPrime2.doubleValue()))) {
|
||||
barhPrime.setValue(hPrimeSum);
|
||||
} else {
|
||||
if (Math.abs(hPrime1 - hPrime2) <= deg180InRad)
|
||||
barhPrime.setValue(hPrimeSum / 2.0);
|
||||
else {
|
||||
if (hPrimeSum < deg360InRad)
|
||||
barhPrime.setValue((hPrimeSum + deg360InRad) / 2.0);
|
||||
else
|
||||
barhPrime.setValue((hPrimeSum - deg360InRad) / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
barCPrime.setValue((CPrime1.doubleValue() + CPrime2.doubleValue()) / 2.0);
|
||||
double T = 1.0 - (0.17 * Math.cos(barhPrime.doubleValue() - deg2Rad(30f))) +
|
||||
(0.24 * Math.cos(2.0 * barhPrime.doubleValue())) +
|
||||
(0.32 * Math.cos((3.0 * barhPrime.doubleValue()) + deg2Rad(6f))) -
|
||||
(0.20 * Math.cos((4.0 * barhPrime.doubleValue()) - deg2Rad(63f)));
|
||||
double S_H = 1 + (0.015f * barCPrime.doubleValue() * T);
|
||||
return (float) (deltaHPrime / (k_H * S_H));
|
||||
}
|
||||
|
||||
static float R_T(final Number barCPrime, final Number barhPrime, final float C_prime_div_k_L_S_L, final float H_prime_div_k_L_S_L) {
|
||||
final double pow25To7 = 6103515625.0; /* Math.pow(25, 7) */
|
||||
double deltaTheta = deg2Rad(30f) * Math.exp(-Math.pow((barhPrime.doubleValue() - deg2Rad(275f)) / deg2Rad(25f), 2.0));
|
||||
double R_C = 2.0 * Math.sqrt(Math.pow(barCPrime.doubleValue(), 7.0) / (Math.pow(barCPrime.doubleValue(), 7.0) + pow25To7));
|
||||
double R_T = (-Math.sin(2.0 * deltaTheta)) * R_C;
|
||||
return (float) (R_T * C_prime_div_k_L_S_L * H_prime_div_k_L_S_L);
|
||||
}
|
||||
|
||||
/* From the paper "The CIEDE2000 Color-Difference Formula: Implementation Notes, */
|
||||
/* Supplementary Test Data, and Mathematical Observations", by */
|
||||
/* Gaurav Sharma, Wencheng Wu and Edul N. Dalal, */
|
||||
/* Color Res. Appl., vol. 30, no. 1, pp. 21-30, Feb. 2005. */
|
||||
/* Return the CIEDE2000 Delta E color difference measure squared, for two Lab values */
|
||||
static float CIEDE2000(final Lab lab1, final Lab lab2) {
|
||||
float deltaL_prime_div_k_L_S_L = L_prime_div_k_L_S_L(lab1, lab2);
|
||||
MutableDouble a1Prime = new MutableDouble(), a2Prime = new MutableDouble(), CPrime1 = new MutableDouble(), CPrime2 = new MutableDouble();
|
||||
float deltaC_prime_div_k_L_S_L = C_prime_div_k_L_S_L(lab1, lab2, a1Prime, a2Prime, CPrime1, CPrime2);
|
||||
MutableDouble barCPrime = new MutableDouble(), barhPrime = new MutableDouble();
|
||||
float deltaH_prime_div_k_L_S_L = H_prime_div_k_L_S_L(lab1, lab2, a1Prime, a2Prime, CPrime1, CPrime2, barCPrime, barhPrime);
|
||||
float deltaR_T = R_T(barCPrime, barhPrime, deltaC_prime_div_k_L_S_L, deltaH_prime_div_k_L_S_L);
|
||||
return (float) (Math.pow(deltaL_prime_div_k_L_S_L, 2.0) +
|
||||
Math.pow(deltaC_prime_div_k_L_S_L, 2.0) +
|
||||
Math.pow(deltaH_prime_div_k_L_S_L, 2.0) +
|
||||
deltaR_T);
|
||||
}
|
||||
|
||||
static double Y_Diff(final int c1, final int c2) {
|
||||
java.util.function.Function<Integer, Double> color2Y = c -> {
|
||||
double sr = gammaToLinear(Color.red(c));
|
||||
double sg = gammaToLinear(Color.green(c));
|
||||
double sb = gammaToLinear(Color.blue(c));
|
||||
return sr * 0.2126 + sg * 0.7152 + sb * 0.0722;
|
||||
};
|
||||
|
||||
double y = color2Y.apply(c1);
|
||||
double y2 = color2Y.apply(c2);
|
||||
return Math.abs(y2 - y) * XYZ_WHITE_REFERENCE_Y;
|
||||
}
|
||||
|
||||
static double U_Diff(final int c1, final int c2) {
|
||||
java.util.function.Function<Integer, Double> color2U = c -> {
|
||||
return -0.09991 * Color.red(c) - 0.33609 * Color.green(c) + 0.436 * Color.blue(c);
|
||||
};
|
||||
|
||||
double u = color2U.apply(c1);
|
||||
double u2 = color2U.apply(c2);
|
||||
return Math.abs(u2 - u);
|
||||
}
|
||||
static double U_Diff(final int c1, final int c2)
|
||||
{
|
||||
java.util.function.Function<Integer, Double> color2U = c -> {
|
||||
return -0.09991 * Color.red(c) - 0.33609 * Color.green(c) + 0.436 * Color.blue(c);
|
||||
};
|
||||
|
||||
double u = color2U.apply(c1);
|
||||
double u2 = color2U.apply(c2);
|
||||
return Math.abs(u2 - u);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.android.nQuant;
|
||||
|
||||
public interface Ditherable {
|
||||
public int getColorIndex(final int c);
|
||||
|
||||
public short nearestColorIndex(final int[] palette, final int c, final int pos);
|
||||
public int getColorIndex(final int c);
|
||||
|
||||
public short nearestColorIndex(final Integer[] palette, final int c, final int pos);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.android.nQuant;
|
||||
/* Generalized Hilbert ("gilbert") space-filling curve for rectangular domains of arbitrary (non-power of two) sizes.
|
||||
Copyright (c) 2021 - 2025 Miller Cy Chan
|
||||
Copyright (c) 2021 - 2026 Miller Cy Chan
|
||||
* A general rectangle with a known orientation is split into three regions ("up", "right", "down"), for which the function calls itself recursively, until a trivial path can be produced. */
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
|
||||
@@ -13,278 +13,362 @@ import static com.android.nQuant.BitmapUtilities.BYTE_MAX;
|
||||
|
||||
public class GilbertCurve {
|
||||
|
||||
private static final class ErrorBox implements Comparable<ErrorBox> {
|
||||
private double yDiff = 0;
|
||||
private final float[] p;
|
||||
private static final class ErrorBox
|
||||
{
|
||||
private double yDiff = 0;
|
||||
private final float[] p;
|
||||
private ErrorBox() {
|
||||
p = new float[4];
|
||||
}
|
||||
|
||||
private ErrorBox() {
|
||||
p = new float[4];
|
||||
}
|
||||
private ErrorBox(int c) {
|
||||
p = new float[] {
|
||||
Color.red(c),
|
||||
Color.green(c),
|
||||
Color.blue(c),
|
||||
Color.alpha(c)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private ErrorBox(int c) {
|
||||
p = new float[]{
|
||||
Color.red(c),
|
||||
Color.green(c),
|
||||
Color.blue(c),
|
||||
Color.alpha(c)
|
||||
};
|
||||
}
|
||||
private byte ditherMax, DITHER_MAX;
|
||||
private float beta;
|
||||
private float[] weights;
|
||||
private final boolean dither, hasAlpha, sortedByYDiff;
|
||||
private final int width, height;
|
||||
private final double weight;
|
||||
private final int[] pixels;
|
||||
private final Integer[] palette;
|
||||
private final int[] qPixels;
|
||||
private final Ditherable ditherable;
|
||||
private final float[] saliencies;
|
||||
private final Queue<ErrorBox> errorq;
|
||||
|
||||
@Override
|
||||
public int compareTo(final ErrorBox other) {
|
||||
return Double.compare(yDiff, other.yDiff);
|
||||
}
|
||||
}
|
||||
private final int margin, thresold;
|
||||
private static final float BLOCK_SIZE = 343f;
|
||||
|
||||
private byte ditherMax, DITHER_MAX;
|
||||
private float beta;
|
||||
private float[] weights;
|
||||
private final boolean dither, sortedByYDiff;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final int[] pixels;
|
||||
private final int[] palette;
|
||||
private final int[] qPixels;
|
||||
private final Ditherable ditherable;
|
||||
private final float[] saliencies;
|
||||
private final Queue<ErrorBox> errorq;
|
||||
private final int[] lookup;
|
||||
private GilbertCurve(final int width, final int height, final int[] image, final Integer[] palette, final int[] qPixels, final Ditherable ditherable, final float[] saliencies, double weight, boolean dither)
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.pixels = image;
|
||||
this.palette = palette;
|
||||
this.qPixels = qPixels;
|
||||
this.ditherable = ditherable;
|
||||
this.hasAlpha = weight < 0;
|
||||
this.saliencies = saliencies;
|
||||
this.dither = dither;
|
||||
this.weight = Math.abs(weight);
|
||||
margin = weight < .0025 ? 12 : weight < .004 ? 8 : 6;
|
||||
sortedByYDiff = palette.length > 128 && weight >= .02 && (!hasAlpha || weight < .18);
|
||||
beta = palette.length > 4 ? (float) (.6f - .00625f * palette.length) : 1;
|
||||
if (palette.length > 4) {
|
||||
double boundary = .005 - .0000625 * palette.length;
|
||||
beta = (float) (weight > boundary ? .25 : Math.min(1.5, beta + palette.length * weight));
|
||||
if (palette.length > 16 && palette.length <= 32 && weight < .003)
|
||||
beta += .075f;
|
||||
else if (weight < .0015 || (palette.length > 32 && palette.length < 256))
|
||||
beta += .1f;
|
||||
if (palette.length >= 64 && (weight > .012 && weight < .0125) || (weight > .025 && weight < .03))
|
||||
beta += .05f;
|
||||
else if (palette.length > 32 && palette.length < 64 && weight < .015)
|
||||
beta = .55f;
|
||||
else if (palette.length > 16 && palette.length <= 32 && weight <= .005)
|
||||
beta += (float) (.05 + weight * palette.length);
|
||||
}
|
||||
else
|
||||
beta *= .95f;
|
||||
|
||||
if (palette.length > 64 || (palette.length > 4 && weight > .02))
|
||||
beta *= .4f;
|
||||
if (palette.length > 64 && weight < .02)
|
||||
beta = .18f;
|
||||
|
||||
private final int margin, thresold;
|
||||
private static final float BLOCK_SIZE = 343f;
|
||||
errorq = sortedByYDiff ? new PriorityQueue<>(new Comparator<ErrorBox>() {
|
||||
|
||||
private GilbertCurve(final int width, final int height, final int[] image, final int[] palette, final int[] qPixels, final Ditherable ditherable, final float[] saliencies, double weight, boolean dither) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.pixels = image;
|
||||
this.palette = palette;
|
||||
this.qPixels = qPixels;
|
||||
this.ditherable = ditherable;
|
||||
boolean hasAlpha = weight < 0;
|
||||
this.saliencies = hasAlpha ? null : saliencies;
|
||||
this.dither = dither;
|
||||
weight = Math.abs(weight);
|
||||
margin = weight < .0025 ? 12 : weight < .004 ? 8 : 6;
|
||||
sortedByYDiff = palette.length > 128 && (!hasAlpha || weight < .18);
|
||||
beta = palette.length > 4 ? (float) (.6f - .00625f * palette.length) : 1;
|
||||
if (palette.length > 4) {
|
||||
double boundary = .005 - .0000625 * palette.length;
|
||||
beta = (float) (weight > boundary ? .25 : Math.min(1.5, beta + palette.length * weight));
|
||||
if (palette.length > 32 && palette.length < 256)
|
||||
beta += .1f;
|
||||
} else
|
||||
beta *= .95f;
|
||||
if (palette.length > 64 || (palette.length > 4 && weight > .02))
|
||||
beta *= .4f;
|
||||
@Override
|
||||
public int compare(ErrorBox o1, ErrorBox o2) {
|
||||
return Double.compare(o2.yDiff, o1.yDiff);
|
||||
}
|
||||
|
||||
}) : new ArrayDeque<>();
|
||||
|
||||
DITHER_MAX = weight < .015 ? (weight > .0025) ? (byte) 25 : 16 : 9;
|
||||
if (weight > .99) {
|
||||
beta = (float) weight;
|
||||
DITHER_MAX = 25;
|
||||
}
|
||||
|
||||
double edge = hasAlpha ? 1 : Math.exp(weight) - .25;
|
||||
double deviation = weight > .002 ? -.25 : 1;
|
||||
ditherMax = (hasAlpha || DITHER_MAX > 9) ? (byte) BitmapUtilities.sqr(Math.sqrt(DITHER_MAX) + edge * deviation) : (byte) (DITHER_MAX * (saliencies != null ? 2 : Math.E));
|
||||
final int density = palette.length > 16 ? 3200 : 1500;
|
||||
if(palette.length / weight > 5000 && (weight > .045 || (weight > .01 && palette.length < 64)))
|
||||
ditherMax = (byte) BitmapUtilities.sqr(5 + edge);
|
||||
else if(weight < .03 && palette.length / weight < density && palette.length >= 16 && palette.length < 256)
|
||||
ditherMax = (byte) BitmapUtilities.sqr(5 + edge);
|
||||
thresold = DITHER_MAX > 9 ? -112 : -64;
|
||||
weights = new float[0];
|
||||
}
|
||||
|
||||
errorq = sortedByYDiff ? new PriorityQueue<>() : new ArrayDeque<>();
|
||||
private static float normalDistribution(float x, float peak) {
|
||||
final float mean = .5f, stdDev = .1f;
|
||||
|
||||
DITHER_MAX = weight < .015 ? (weight > .0025) ? (byte) 25 : 16 : 9;
|
||||
double edge = hasAlpha ? 1 : Math.exp(weight) - .25;
|
||||
double deviation = weight > .002 ? -.25 : 1;
|
||||
ditherMax = (hasAlpha || DITHER_MAX > 9) ? (byte) BitmapUtilities.sqr(Math.sqrt(DITHER_MAX) + edge * deviation) : (byte) (DITHER_MAX * Math.E);
|
||||
final int density = palette.length > 16 ? 3200 : 1500;
|
||||
if (palette.length / weight > 5000 && (weight > .045 || (weight > .01 && palette.length < 64)))
|
||||
ditherMax = (byte) BitmapUtilities.sqr(5 + edge);
|
||||
else if (weight < .03 && palette.length / weight < density && palette.length >= 16 && palette.length < 256)
|
||||
ditherMax = (byte) BitmapUtilities.sqr(5 + edge);
|
||||
thresold = DITHER_MAX > 9 ? -112 : -64;
|
||||
weights = new float[0];
|
||||
lookup = new int[65536];
|
||||
}
|
||||
// Calculate the probability density function (PDF)
|
||||
double exponent = -Math.pow(x - mean, 2) / (2 * Math.pow(stdDev, 2));
|
||||
double pdf = (1 / (stdDev * Math.sqrt(2 * Math.PI))) * Math.exp(exponent);
|
||||
double maxPdf = 1 / (stdDev * Math.sqrt(2 * Math.PI)); // Peak at x = mean
|
||||
double scaledPdf = (pdf / maxPdf) * peak;
|
||||
return (float) Math.max(0.0, Math.min(peak, scaledPdf));
|
||||
}
|
||||
|
||||
private void ditherPixel(int x, int y) {
|
||||
final int bidx = x + y * width;
|
||||
final int pixel = pixels[bidx];
|
||||
ErrorBox error = new ErrorBox(pixel);
|
||||
private int ditherPixel(int x, int y, int c2, float beta) {
|
||||
final int bidx = x + y * width;
|
||||
final int pixel = pixels[bidx];
|
||||
int r_pix = Color.red(c2);
|
||||
int g_pix = Color.green(c2);
|
||||
int b_pix = Color.blue(c2);
|
||||
int a_pix = Color.alpha(c2);
|
||||
|
||||
final float strength = 1 / 3f;
|
||||
final int acceptedDiff = Math.max(2, palette.length - margin);
|
||||
if (palette.length <= 4 && saliencies[bidx] > .2f && saliencies[bidx] < .25f)
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], beta * 2 / saliencies[bidx], strength, x, y);
|
||||
else if (palette.length <= 4 || CIELABConvertor.Y_Diff(pixel, c2) < (2 * acceptedDiff)) {
|
||||
if (palette.length > 64) {
|
||||
float kappa = saliencies[bidx] < .6f ? beta * .15f / saliencies[bidx] : beta * .4f / saliencies[bidx];
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], kappa, strength, x, y);
|
||||
}
|
||||
else if (palette.length > 16 && weight < .005)
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], beta * normalDistribution(saliencies[bidx], .5f) + beta, strength, x, y);
|
||||
else
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], beta * .5f / saliencies[bidx], strength, x, y);
|
||||
}
|
||||
|
||||
float maxErr = DITHER_MAX - 1;
|
||||
int i = sortedByYDiff ? weights.length - 1 : 0;
|
||||
for (ErrorBox eb : errorq) {
|
||||
if (i < 0 || i >= weights.length)
|
||||
break;
|
||||
double gamma = (palette.length <= 32 && weight < .01 && weight > .007) ? 1 - beta : beta;
|
||||
if (palette.length > 4 && CIELABConvertor.Y_Diff(pixel, c2) > (gamma * acceptedDiff)) {
|
||||
if (margin > 6 || gamma > beta) {
|
||||
float kappa = saliencies[bidx] < .4f ? beta * .4f * saliencies[bidx] : beta * .4f / saliencies[bidx];
|
||||
int c1 = Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
if (palette.length > 32 && saliencies[bidx] < .9)
|
||||
kappa = beta * normalDistribution(saliencies[bidx], 2f);
|
||||
else {
|
||||
if (weight >= .0015 && saliencies[bidx] < .6)
|
||||
c1 = pixel;
|
||||
if (weight >= .005 && saliencies[bidx] < .6)
|
||||
kappa = beta * normalDistribution(saliencies[bidx], weight < .0008 ? 2.5f : 1.75f);
|
||||
else if (palette.length >= 32 || CIELABConvertor.Y_Diff(c1, c2) > (gamma * Math.PI * acceptedDiff)) {
|
||||
double ub = 1 - palette.length / 320.0;
|
||||
if (saliencies[bidx] > .15 && saliencies[bidx] < ub)
|
||||
kappa = beta * (!sortedByYDiff && weight < .0025 ? .55f : .5f) / saliencies[bidx];
|
||||
else
|
||||
kappa = beta * normalDistribution(saliencies[bidx], weight < .0025 ? 1.82f : 2f);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < eb.p.length; ++j) {
|
||||
error.p[j] += eb.p[j] * weights[i];
|
||||
if (error.p[j] > maxErr)
|
||||
maxErr = error.p[j];
|
||||
}
|
||||
i += sortedByYDiff ? -1 : 1;
|
||||
}
|
||||
c2 = BlueNoise.diffuse(c1, palette[qPixels[bidx]], kappa, strength, x, y);
|
||||
}
|
||||
else if (palette.length <= 32 && weight >= .004)
|
||||
c2 = BlueNoise.diffuse(c2, palette[qPixels[bidx]], beta * normalDistribution(saliencies[bidx], .25f), strength, x, y);
|
||||
else
|
||||
c2 = Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
}
|
||||
|
||||
if (DITHER_MAX < 16 && palette.length > 4 && saliencies[bidx] < .6f && CIELABConvertor.Y_Diff(pixel, c2) > margin - 1)
|
||||
c2 = Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
if (palette.length > 32 && saliencies[bidx] > .95) {
|
||||
float kappa = beta * Math.max(.05f, .75f - palette.length / 128f) * saliencies[bidx];
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], kappa, strength, x, y);
|
||||
}
|
||||
|
||||
int r_pix = (int) Math.min(BYTE_MAX, Math.max(error.p[0], 0.0));
|
||||
int g_pix = (int) Math.min(BYTE_MAX, Math.max(error.p[1], 0.0));
|
||||
int b_pix = (int) Math.min(BYTE_MAX, Math.max(error.p[2], 0.0));
|
||||
int a_pix = (int) Math.min(BYTE_MAX, Math.max(error.p[3], 0.0));
|
||||
return ditherable.nearestColorIndex(palette, c2, bidx);
|
||||
}
|
||||
|
||||
int c2 = Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
if (saliencies != null && dither && !sortedByYDiff) {
|
||||
final float strength = 1 / 3f;
|
||||
final int acceptedDiff = Math.max(2, palette.length - margin);
|
||||
if (palette.length <= 4 && saliencies[bidx] > .2f && saliencies[bidx] < .25f)
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], beta * 2 / saliencies[bidx], strength, x, y);
|
||||
else if (palette.length <= 4 || CIELABConvertor.Y_Diff(pixel, c2) < (2 * acceptedDiff)) {
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], beta * .5f / saliencies[bidx], strength, x, y);
|
||||
if (palette.length <= 4 && CIELABConvertor.U_Diff(pixel, c2) > (8 * acceptedDiff)) {
|
||||
int c1 = saliencies[bidx] > .65f ? pixel : Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
c2 = BlueNoise.diffuse(c1, palette[qPixels[bidx]], beta * saliencies[bidx], strength, x, y);
|
||||
}
|
||||
if (CIELABConvertor.U_Diff(pixel, c2) > (margin * acceptedDiff))
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], beta / saliencies[bidx], strength, x, y);
|
||||
}
|
||||
private void diffusePixel(int x, int y) {
|
||||
final int bidx = x + y * width;
|
||||
final int pixel = pixels[bidx];
|
||||
ErrorBox error = new ErrorBox(pixel);
|
||||
|
||||
if (palette.length < 3 || margin > 6) {
|
||||
if (palette.length > 4 && (CIELABConvertor.Y_Diff(pixel, c2) > (beta * acceptedDiff) || CIELABConvertor.U_Diff(pixel, c2) > (2 * acceptedDiff))) {
|
||||
float kappa = saliencies[bidx] < .4f ? beta * .4f * saliencies[bidx] : beta * .4f / saliencies[bidx];
|
||||
int c1 = saliencies[bidx] < .6f ? pixel : Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
c2 = BlueNoise.diffuse(c1, palette[qPixels[bidx]], kappa, strength, x, y);
|
||||
}
|
||||
} else if (palette.length > 4 && (CIELABConvertor.Y_Diff(pixel, c2) > (beta * acceptedDiff) || CIELABConvertor.U_Diff(pixel, c2) > acceptedDiff)) {
|
||||
if (beta < .3f && (palette.length <= 32 || saliencies[bidx] < beta))
|
||||
c2 = BlueNoise.diffuse(c2, palette[qPixels[bidx]], beta * .4f * saliencies[bidx], strength, x, y);
|
||||
else
|
||||
c2 = Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
}
|
||||
float maxErr = DITHER_MAX - 1;
|
||||
int i = sortedByYDiff ? weights.length - 1 : 0;
|
||||
for (ErrorBox eb : errorq) {
|
||||
if (i < 0 || i >= weights.length)
|
||||
break;
|
||||
|
||||
if (DITHER_MAX < 16 && saliencies[bidx] < .6f && CIELABConvertor.Y_Diff(pixel, c2) > margin - 1)
|
||||
c2 = Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
for (int j = 0; j < eb.p.length; ++j) {
|
||||
error.p[j] += eb.p[j] * weights[i];
|
||||
if(error.p[j] > maxErr)
|
||||
maxErr = error.p[j];
|
||||
}
|
||||
i += sortedByYDiff ? -1 : 1;
|
||||
}
|
||||
|
||||
int offset = ditherable.getColorIndex(c2);
|
||||
if (lookup[offset] == 0)
|
||||
lookup[offset] = ditherable.nearestColorIndex(palette, c2, bidx) + 1;
|
||||
qPixels[bidx] = palette[lookup[offset] - 1];
|
||||
} else if (palette.length <= 32 && a_pix > 0xF0) {
|
||||
int offset = ditherable.getColorIndex(c2);
|
||||
if (lookup[offset] == 0)
|
||||
lookup[offset] = ditherable.nearestColorIndex(palette, c2, bidx) + 1;
|
||||
qPixels[bidx] = palette[lookup[offset] - 1];
|
||||
int r_pix = (int) Math.min(BYTE_MAX, Math.max(error.p[0], 0.0));
|
||||
int g_pix = (int) Math.min(BYTE_MAX, Math.max(error.p[1], 0.0));
|
||||
int b_pix = (int) Math.min(BYTE_MAX, Math.max(error.p[2], 0.0));
|
||||
int a_pix = (int) Math.min(BYTE_MAX, Math.max(error.p[3], 0.0));
|
||||
|
||||
final int acceptedDiff = Math.max(2, palette.length - margin);
|
||||
if (saliencies != null && (CIELABConvertor.Y_Diff(pixel, c2) > acceptedDiff || CIELABConvertor.U_Diff(pixel, c2) > (2 * acceptedDiff))) {
|
||||
final float strength = 1 / 3f;
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], 1 / saliencies[bidx], strength, x, y);
|
||||
qPixels[bidx] = palette[ditherable.nearestColorIndex(palette, c2, bidx)];
|
||||
}
|
||||
} else
|
||||
qPixels[bidx] = palette[ditherable.nearestColorIndex(palette, c2, bidx)];
|
||||
int c2 = Color.argb(a_pix, r_pix, g_pix, b_pix);
|
||||
if (saliencies != null && dither && !sortedByYDiff && (!hasAlpha || Color.alpha(pixel) < a_pix)) {
|
||||
if (palette.length >= 256 && saliencies[bidx] > .99f)
|
||||
qPixels[bidx] = ditherable.nearestColorIndex(palette, c2, bidx);
|
||||
else
|
||||
qPixels[bidx] = ditherPixel(x, y, c2, beta);
|
||||
}
|
||||
else if (palette.length <= 32 && a_pix > 0xF0) {
|
||||
qPixels[bidx] = ditherable.nearestColorIndex(palette, c2, bidx);
|
||||
|
||||
if (errorq.size() >= DITHER_MAX)
|
||||
errorq.poll();
|
||||
else if (!errorq.isEmpty())
|
||||
initWeights(errorq.size());
|
||||
final int acceptedDiff = Math.max(2, palette.length - margin);
|
||||
if(saliencies != null && (CIELABConvertor.Y_Diff(pixel, c2) > acceptedDiff || CIELABConvertor.U_Diff(pixel, c2) > (2 * acceptedDiff))) {
|
||||
final float strength = 1 / 3f;
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], 1 / saliencies[bidx], strength, x, y);
|
||||
qPixels[bidx] = ditherable.nearestColorIndex(palette, c2, bidx);
|
||||
}
|
||||
}
|
||||
else
|
||||
qPixels[bidx] = ditherable.nearestColorIndex(palette, c2, bidx);
|
||||
|
||||
c2 = qPixels[bidx];
|
||||
error.p[0] = r_pix - Color.red(c2);
|
||||
error.p[1] = g_pix - Color.green(c2);
|
||||
error.p[2] = b_pix - Color.blue(c2);
|
||||
error.p[3] = a_pix - Color.alpha(c2);
|
||||
if(errorq.size() >= DITHER_MAX)
|
||||
errorq.poll();
|
||||
else if(!errorq.isEmpty())
|
||||
initWeights(errorq.size());
|
||||
|
||||
boolean denoise = palette.length > 2;
|
||||
boolean diffuse = BlueNoise.TELL_BLUE_NOISE[bidx & 4095] > thresold;
|
||||
error.yDiff = sortedByYDiff ? CIELABConvertor.Y_Diff(pixel, c2) : 1;
|
||||
boolean illusion = !diffuse && BlueNoise.TELL_BLUE_NOISE[(int) (error.yDiff * 4096) & 4095] > thresold;
|
||||
c2 = palette[qPixels[bidx]];
|
||||
error.p[0] = r_pix - Color.red(c2);
|
||||
error.p[1] = g_pix - Color.green(c2);
|
||||
error.p[2] = b_pix - Color.blue(c2);
|
||||
error.p[3] = a_pix - Color.alpha(c2);
|
||||
|
||||
int errLength = denoise ? error.p.length - 1 : 0;
|
||||
for (int j = 0; j < errLength; ++j) {
|
||||
if (Math.abs(error.p[j]) >= ditherMax) {
|
||||
if (diffuse)
|
||||
error.p[j] = (float) Math.tanh(error.p[j] / maxErr * 20) * (ditherMax - 1);
|
||||
else if (illusion)
|
||||
error.p[j] = (float) (error.p[j] / maxErr * error.yDiff) * (ditherMax - 1);
|
||||
else
|
||||
error.p[j] /= (float) (1 + Math.sqrt(ditherMax));
|
||||
}
|
||||
}
|
||||
errorq.add(error);
|
||||
}
|
||||
boolean denoise = palette.length > 2;
|
||||
boolean diffuse = BlueNoise.TELL_BLUE_NOISE[bidx & 4095] > thresold;
|
||||
error.yDiff = sortedByYDiff ? CIELABConvertor.Y_Diff(pixel, c2) : 1;
|
||||
boolean illusion = !diffuse && BlueNoise.TELL_BLUE_NOISE[(int) (error.yDiff * 4096) & 4095] > thresold;
|
||||
|
||||
private void generate2d(int x, int y, int ax, int ay, int bx, int by) {
|
||||
int w = Math.abs(ax + ay);
|
||||
int h = Math.abs(bx + by);
|
||||
int dax = Integer.signum(ax);
|
||||
int day = Integer.signum(ay);
|
||||
int dbx = Integer.signum(bx);
|
||||
int dby = Integer.signum(by);
|
||||
boolean unaccepted = false;
|
||||
int errLength = denoise ? error.p.length - 1 : 0;
|
||||
for (int j = 0; j < errLength; ++j) {
|
||||
if (Math.abs(error.p[j]) >= ditherMax) {
|
||||
if (sortedByYDiff && saliencies != null)
|
||||
unaccepted = true;
|
||||
|
||||
if (h == 1) {
|
||||
for (int i = 0; i < w; ++i) {
|
||||
ditherPixel(x, y);
|
||||
x += dax;
|
||||
y += day;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (diffuse)
|
||||
error.p[j] = (float) Math.tanh(error.p[j] / maxErr * 20) * (ditherMax - 1);
|
||||
else if(illusion)
|
||||
error.p[j] = (float) (error.p[j] / maxErr * error.yDiff) * (ditherMax - 1);
|
||||
else
|
||||
error.p[j] /= (float) (1 + Math.sqrt(ditherMax));
|
||||
}
|
||||
|
||||
if (w == 1) {
|
||||
for (int i = 0; i < h; ++i) {
|
||||
ditherPixel(x, y);
|
||||
x += dbx;
|
||||
y += dby;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (sortedByYDiff && saliencies == null && Math.abs(error.p[j]) >= DITHER_MAX)
|
||||
unaccepted = true;
|
||||
}
|
||||
|
||||
int ax2 = ax / 2;
|
||||
int ay2 = ay / 2;
|
||||
int bx2 = bx / 2;
|
||||
int by2 = by / 2;
|
||||
if (unaccepted) {
|
||||
if (saliencies != null)
|
||||
qPixels[bidx] = ditherPixel(x, y, c2, beta);
|
||||
else if (CIELABConvertor.Y_Diff(pixel, c2) > 3 && CIELABConvertor.U_Diff(pixel, c2) > 3) {
|
||||
final float strength = 1 / 3f;
|
||||
c2 = BlueNoise.diffuse(pixel, palette[qPixels[bidx]], strength, strength, x, y);
|
||||
qPixels[bidx] = ditherable.nearestColorIndex(palette, c2, bidx);
|
||||
}
|
||||
}
|
||||
|
||||
int w2 = Math.abs(ax2 + ay2);
|
||||
int h2 = Math.abs(bx2 + by2);
|
||||
errorq.add(error);
|
||||
|
||||
if (2 * w > 3 * h) {
|
||||
if ((w2 % 2) != 0 && w > 2) {
|
||||
ax2 += dax;
|
||||
ay2 += day;
|
||||
}
|
||||
generate2d(x, y, ax2, ay2, bx, by);
|
||||
generate2d(x + ax2, y + ay2, ax - ax2, ay - ay2, bx, by);
|
||||
return;
|
||||
}
|
||||
if (dither || palette.length <= 32)
|
||||
qPixels[bidx] = palette[qPixels[bidx]];
|
||||
}
|
||||
|
||||
if ((h2 % 2) != 0 && h > 2) {
|
||||
bx2 += dbx;
|
||||
by2 += dby;
|
||||
}
|
||||
private void generate2d(int x, int y, int ax, int ay, int bx, int by) throws Exception {
|
||||
int w = Math.abs(ax + ay);
|
||||
int h = Math.abs(bx + by);
|
||||
int dax = Integer.signum(ax);
|
||||
int day = Integer.signum(ay);
|
||||
int dbx = Integer.signum(bx);
|
||||
int dby = Integer.signum(by);
|
||||
|
||||
generate2d(x, y, bx2, by2, ax2, ay2);
|
||||
generate2d(x + bx2, y + by2, ax, ay, bx - bx2, by - by2);
|
||||
generate2d(x + (ax - dax) + (bx2 - dbx), y + (ay - day) + (by2 - dby), -bx2, -by2, -(ax - ax2), -(ay - ay2));
|
||||
}
|
||||
if (h == 1) {
|
||||
for (int i = 0; i < w; ++i){
|
||||
diffusePixel(x, y);
|
||||
x += dax;
|
||||
y += day;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private void initWeights(int size) {
|
||||
/* Dithers all pixels of the image in sequence using
|
||||
* the Gilbert path, and distributes the error in
|
||||
* a sequence of pixels size.
|
||||
*/
|
||||
final float weightRatio = (float) Math.pow(BLOCK_SIZE + 1f, 1f / (size - 1f));
|
||||
float weight = 1f, sumweight = 0f;
|
||||
weights = new float[size];
|
||||
for (int c = 0; c < size; ++c) {
|
||||
errorq.add(new ErrorBox());
|
||||
sumweight += (weights[size - c - 1] = weight);
|
||||
weight /= weightRatio;
|
||||
}
|
||||
if (w == 1) {
|
||||
for (int i = 0; i < h; ++i){
|
||||
diffusePixel(x, y);
|
||||
x += dbx;
|
||||
y += dby;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
weight = 0f; /* Normalize */
|
||||
for (int c = 0; c < size; ++c)
|
||||
weight += (weights[c] /= sumweight);
|
||||
weights[0] += 1f - weight;
|
||||
}
|
||||
int ax2 = ax / 2;
|
||||
int ay2 = ay / 2;
|
||||
int bx2 = bx / 2;
|
||||
int by2 = by / 2;
|
||||
|
||||
private void run() {
|
||||
if (!sortedByYDiff)
|
||||
initWeights(DITHER_MAX);
|
||||
int w2 = Math.abs(ax2 + ay2);
|
||||
int h2 = Math.abs(bx2 + by2);
|
||||
|
||||
if (width >= height)
|
||||
generate2d(0, 0, width, 0, 0, height);
|
||||
else
|
||||
generate2d(0, 0, 0, height, width, 0);
|
||||
}
|
||||
if (2 * w > 3 * h) {
|
||||
if ((w2 % 2) != 0 && w > 2) {
|
||||
ax2 += dax;
|
||||
ay2 += day;
|
||||
}
|
||||
generate2d(x, y, ax2, ay2, bx, by);
|
||||
generate2d(x + ax2, y + ay2, ax - ax2, ay - ay2, bx, by);
|
||||
return;
|
||||
}
|
||||
|
||||
public static int[] dither(final int width, final int height, final int[] pixels, final int[] palette, final Ditherable ditherable, final float[] saliencies, final double weight, final boolean dither) {
|
||||
if ((h2 % 2) != 0 && h > 2) {
|
||||
bx2 += dbx;
|
||||
by2 += dby;
|
||||
}
|
||||
|
||||
generate2d(x, y, bx2, by2, ax2, ay2);
|
||||
generate2d(x + bx2, y + by2, ax, ay, bx - bx2, by - by2);
|
||||
generate2d(x + (ax - dax) + (bx2 - dbx), y + (ay - day) + (by2 - dby), -bx2, -by2, -(ax - ax2), -(ay - ay2));
|
||||
}
|
||||
|
||||
private void initWeights(int size) {
|
||||
/* Dithers all pixels of the image in sequence using
|
||||
* the Gilbert path, and distributes the error in
|
||||
* a sequence of pixels size.
|
||||
*/
|
||||
final float weightRatio = (float) Math.pow(BLOCK_SIZE + 1f, 1f / (size - 1f));
|
||||
float weight = 1f, sumweight = 0f;
|
||||
weights = new float[size];
|
||||
for(int c = 0; c < size; ++c) {
|
||||
errorq.add(new ErrorBox());
|
||||
sumweight += (weights[size - c - 1] = weight);
|
||||
weight /= weightRatio;
|
||||
}
|
||||
|
||||
weight = 0f; /* Normalize */
|
||||
for(int c = 0; c < size; ++c)
|
||||
weight += (weights[c] /= sumweight);
|
||||
weights[0] += 1f - weight;
|
||||
}
|
||||
|
||||
private void run() throws Exception
|
||||
{
|
||||
if(!sortedByYDiff)
|
||||
initWeights(DITHER_MAX);
|
||||
|
||||
if (width >= height)
|
||||
generate2d(0, 0, width, 0, 0, height);
|
||||
else
|
||||
generate2d(0, 0, 0, height, width, 0);
|
||||
}
|
||||
|
||||
public static int[] dither(final int width, final int height, final int[] pixels, final Integer[] palette, final Ditherable ditherable, final float[] saliencies, final double weight, final boolean dither) throws Exception
|
||||
{
|
||||
int[] qPixels = new int[pixels.length];
|
||||
new GilbertCurve(width, height, pixels, palette, qPixels, ditherable, saliencies, weight, dither).run();
|
||||
return qPixels;
|
||||
}
|
||||
new GilbertCurve(width, height, pixels, palette, qPixels, ditherable, saliencies, weight, dither).run();
|
||||
|
||||
return qPixels;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,449 +1,462 @@
|
||||
package com.android.nQuant;
|
||||
/* Fast pairwise nearest neighbor based algorithm for multilevel thresholding
|
||||
Copyright (C) 2004-2016 Mark Tyler and Dmitry Groshev
|
||||
Copyright (c) 2018-2023 Miller Cy Chan
|
||||
Copyright (c) 2018-2026 Miller Cy Chan
|
||||
* error measure; time used is proportional to number of bins squared - WJ */
|
||||
|
||||
import static com.android.nQuant.BitmapUtilities.BYTE_MAX;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.android.nQuant.BitmapUtilities.BYTE_MAX;
|
||||
|
||||
public class PnnQuantizer {
|
||||
protected short alphaThreshold = 0xF;
|
||||
protected boolean hasSemiTransparency = false;
|
||||
protected int m_transparentPixelIndex = -1;
|
||||
protected final int width, height;
|
||||
protected final int[] pixels;
|
||||
protected Integer m_transparentColor = Color.argb(0, BYTE_MAX, BYTE_MAX, BYTE_MAX);
|
||||
|
||||
protected double PR = 0.299, PG = 0.587, PB = 0.114, PA = .3333;
|
||||
protected double ratio = .5, weight = 1;
|
||||
protected static final float[][] coeffs = new float[][]{
|
||||
{0.299f, 0.587f, 0.114f},
|
||||
{-0.14713f, -0.28886f, 0.436f},
|
||||
{0.615f, -0.51499f, -0.10001f}
|
||||
};
|
||||
|
||||
protected Map<Integer, int[]> closestMap = new HashMap<>();
|
||||
protected Map<Integer, Short> nearestMap = new HashMap<>();
|
||||
|
||||
public PnnQuantizer(Bitmap bitmap) {
|
||||
width = bitmap.getWidth();
|
||||
height = bitmap.getHeight();
|
||||
pixels = new int[width * height];
|
||||
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
|
||||
}
|
||||
|
||||
private static final class Pnnbin {
|
||||
double ac = 0, rc = 0, gc = 0, bc = 0;
|
||||
float cnt = 0, err = 0;
|
||||
int nn, fw, bk, tm, mtm;
|
||||
}
|
||||
|
||||
private void find_nn(Pnnbin[] bins, int idx) {
|
||||
int nn = 0;
|
||||
double err = 1e100;
|
||||
|
||||
Pnnbin bin1 = bins[idx];
|
||||
float n1 = bin1.cnt;
|
||||
double wa = bin1.ac;
|
||||
double wr = bin1.rc;
|
||||
double wg = bin1.gc;
|
||||
double wb = bin1.bc;
|
||||
|
||||
int start = 0;
|
||||
if (BlueNoise.TELL_BLUE_NOISE[idx & 4095] > -88)
|
||||
start = (PG < coeffs[0][1]) ? coeffs.length : 1;
|
||||
|
||||
for (int i = bin1.fw; i != 0; i = bins[i].fw) {
|
||||
double n2 = bins[i].cnt, nerr2 = (n1 * n2) / (n1 + n2);
|
||||
if (nerr2 >= err)
|
||||
continue;
|
||||
|
||||
double nerr = 0.0;
|
||||
if (hasSemiTransparency) {
|
||||
start = 1;
|
||||
nerr += nerr2 * (1 - ratio) * PA * BitmapUtilities.sqr(bins[i].ac - wa);
|
||||
if (nerr >= err)
|
||||
continue;
|
||||
}
|
||||
|
||||
nerr += nerr2 * (1 - ratio) * PR * BitmapUtilities.sqr(bins[i].rc - wr);
|
||||
if (nerr >= err)
|
||||
continue;
|
||||
|
||||
nerr += nerr2 * (1 - ratio) * PG * BitmapUtilities.sqr(bins[i].gc - wg);
|
||||
if (nerr >= err)
|
||||
continue;
|
||||
|
||||
nerr += nerr2 * (1 - ratio) * PB * BitmapUtilities.sqr(bins[i].bc - wb);
|
||||
if (nerr >= err)
|
||||
continue;
|
||||
|
||||
for (int j = start; j < coeffs.length; ++j) {
|
||||
nerr += nerr2 * ratio * BitmapUtilities.sqr(coeffs[j][0] * (bins[i].rc - wr));
|
||||
if (nerr >= err)
|
||||
break;
|
||||
|
||||
nerr += nerr2 * ratio * BitmapUtilities.sqr(coeffs[j][1] * (bins[i].gc - wg));
|
||||
if (nerr >= err)
|
||||
break;
|
||||
|
||||
nerr += nerr2 * ratio * BitmapUtilities.sqr(coeffs[j][2] * (bins[i].bc - wb));
|
||||
if (nerr >= err)
|
||||
break;
|
||||
}
|
||||
|
||||
err = nerr;
|
||||
nn = i;
|
||||
}
|
||||
bin1.err = (float) err;
|
||||
bin1.nn = nn;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface QuanFn {
|
||||
float get(float cnt);
|
||||
}
|
||||
|
||||
protected QuanFn getQuanFn(int nMaxColors, short quan_rt) {
|
||||
if (quan_rt > 0) {
|
||||
if (nMaxColors < 64)
|
||||
return cnt -> (float) Math.sqrt(cnt);
|
||||
return cnt -> (int) Math.sqrt(cnt);
|
||||
}
|
||||
if (quan_rt < 0)
|
||||
return cnt -> (int) Math.cbrt(cnt);
|
||||
return cnt -> cnt;
|
||||
}
|
||||
|
||||
protected int[] pnnquan(final int[] pixels, int nMaxColors) {
|
||||
short quan_rt = (short) 1;
|
||||
Pnnbin[] bins = new Pnnbin[65536];
|
||||
|
||||
/* Build histogram */
|
||||
for (int pixel : pixels) {
|
||||
if (Color.alpha(pixel) <= alphaThreshold)
|
||||
pixel = m_transparentColor;
|
||||
|
||||
int index = BitmapUtilities.getColorIndex(pixel, hasSemiTransparency, nMaxColors < 64 || m_transparentPixelIndex >= 0);
|
||||
|
||||
if (bins[index] == null)
|
||||
bins[index] = new Pnnbin();
|
||||
Pnnbin tb = bins[index];
|
||||
tb.ac += Color.alpha(pixel);
|
||||
tb.rc += Color.red(pixel);
|
||||
tb.gc += Color.green(pixel);
|
||||
tb.bc += Color.blue(pixel);
|
||||
tb.cnt++;
|
||||
}
|
||||
|
||||
/* Cluster nonempty bins at one end of array */
|
||||
int maxbins = 0;
|
||||
|
||||
for (int i = 0; i < bins.length; ++i) {
|
||||
if (bins[i] == null)
|
||||
continue;
|
||||
|
||||
float d = 1f / bins[i].cnt;
|
||||
bins[i].ac *= d;
|
||||
bins[i].rc *= d;
|
||||
bins[i].gc *= d;
|
||||
bins[i].bc *= d;
|
||||
|
||||
bins[maxbins++] = bins[i];
|
||||
}
|
||||
|
||||
if (nMaxColors < 16)
|
||||
quan_rt = -1;
|
||||
|
||||
weight = Math.min(0.9, nMaxColors * 1.0 / maxbins);
|
||||
if (weight < .03 && PG >= coeffs[0][1]) {
|
||||
PR = PG = PB = PA = 1;
|
||||
if (nMaxColors >= 64)
|
||||
quan_rt = 0;
|
||||
}
|
||||
|
||||
QuanFn quanFn = getQuanFn(nMaxColors, quan_rt);
|
||||
|
||||
int j = 0;
|
||||
for (; j < maxbins - 1; ++j) {
|
||||
bins[j].fw = j + 1;
|
||||
bins[j + 1].bk = j;
|
||||
|
||||
bins[j].cnt = quanFn.get(bins[j].cnt);
|
||||
}
|
||||
bins[j].cnt = quanFn.get(bins[j].cnt);
|
||||
|
||||
int h, l, l2;
|
||||
/* Initialize nearest neighbors and build heap of them */
|
||||
int[] heap = new int[bins.length + 1];
|
||||
for (int i = 0; i < maxbins; i++) {
|
||||
find_nn(bins, i);
|
||||
/* Push slot on heap */
|
||||
float err = bins[i].err;
|
||||
for (l = ++heap[0]; l > 1; l = l2) {
|
||||
l2 = l >> 1;
|
||||
if (bins[h = heap[l2]].err <= err)
|
||||
break;
|
||||
heap[l] = h;
|
||||
}
|
||||
heap[l] = i;
|
||||
}
|
||||
|
||||
/* Merge bins which increase error the least */
|
||||
int extbins = maxbins - nMaxColors;
|
||||
for (int i = 0; i < extbins; ) {
|
||||
Pnnbin tb = null;
|
||||
/* Use heap to find which bins to merge */
|
||||
for (; ; ) {
|
||||
int b1 = heap[1];
|
||||
tb = bins[b1]; /* One with least error */
|
||||
/* Is stored error up to date? */
|
||||
if ((tb.tm >= tb.mtm) && (bins[tb.nn].mtm <= tb.tm))
|
||||
break;
|
||||
if (tb.mtm == 0xFFFF) /* Deleted node */
|
||||
b1 = heap[1] = heap[heap[0]--];
|
||||
else /* Too old error value */ {
|
||||
find_nn(bins, b1);
|
||||
tb.tm = i;
|
||||
}
|
||||
/* Push slot down */
|
||||
float err = bins[b1].err;
|
||||
for (l = 1; (l2 = l + l) <= heap[0]; l = l2) {
|
||||
if ((l2 < heap[0]) && (bins[heap[l2]].err > bins[heap[l2 + 1]].err))
|
||||
++l2;
|
||||
if (err <= bins[h = heap[l2]].err)
|
||||
break;
|
||||
heap[l] = h;
|
||||
}
|
||||
heap[l] = b1;
|
||||
}
|
||||
|
||||
/* Do a merge */
|
||||
Pnnbin nb = bins[tb.nn];
|
||||
float n1 = tb.cnt;
|
||||
float n2 = nb.cnt;
|
||||
float d = 1f / (n1 + n2);
|
||||
tb.ac = d * Math.round(n1 * tb.ac + n2 * nb.ac);
|
||||
tb.rc = d * Math.round(n1 * tb.rc + n2 * nb.rc);
|
||||
tb.gc = d * Math.round(n1 * tb.gc + n2 * nb.gc);
|
||||
tb.bc = d * Math.round(n1 * tb.bc + n2 * nb.bc);
|
||||
tb.cnt += n2;
|
||||
tb.mtm = ++i;
|
||||
|
||||
/* Unchain deleted bin */
|
||||
bins[nb.bk].fw = nb.fw;
|
||||
bins[nb.fw].bk = nb.bk;
|
||||
nb.mtm = 0xFFFF;
|
||||
}
|
||||
|
||||
/* Fill palette */
|
||||
int[] palette = new int[extbins > 0 ? nMaxColors : maxbins];
|
||||
short k = 0;
|
||||
for (int i = 0; ; ++k) {
|
||||
palette[k] = Color.argb((int) bins[i].ac, (int) bins[i].rc, (int) bins[i].gc, (int) bins[i].bc);
|
||||
|
||||
if ((i = bins[i].fw) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
||||
protected short nearestColorIndex(final int[] palette, int c, final int pos) {
|
||||
Short got = nearestMap.get(c);
|
||||
if (got != null)
|
||||
return got;
|
||||
|
||||
short k = 0;
|
||||
if (Color.alpha(c) <= alphaThreshold)
|
||||
c = m_transparentColor;
|
||||
if (palette.length > 2 && hasAlpha() && Color.alpha(c) > alphaThreshold)
|
||||
k = 1;
|
||||
|
||||
double pr = PR, pg = PG, pb = PB, pa = PA;
|
||||
if (palette.length < 3)
|
||||
pr = pg = pb = pa = 1;
|
||||
|
||||
double mindist = Integer.MAX_VALUE;
|
||||
for (short i = k; i < palette.length; ++i) {
|
||||
int c2 = palette[i];
|
||||
|
||||
double curdist = pa * BitmapUtilities.sqr(Color.alpha(c2) - Color.alpha(c));
|
||||
if (curdist > mindist)
|
||||
continue;
|
||||
|
||||
curdist += pr * BitmapUtilities.sqr(Color.red(c2) - Color.red(c));
|
||||
if (curdist > mindist)
|
||||
continue;
|
||||
|
||||
curdist += pg * BitmapUtilities.sqr(Color.green(c2) - Color.green(c));
|
||||
if (curdist > mindist)
|
||||
continue;
|
||||
|
||||
curdist += pb * BitmapUtilities.sqr(Color.blue(c2) - Color.blue(c));
|
||||
if (curdist > mindist)
|
||||
continue;
|
||||
|
||||
mindist = curdist;
|
||||
k = i;
|
||||
}
|
||||
nearestMap.put(c, k);
|
||||
return k;
|
||||
}
|
||||
|
||||
protected short closestColorIndex(final int[] palette, int c, final int pos) {
|
||||
short k = 0;
|
||||
if (Color.alpha(c) <= alphaThreshold)
|
||||
return nearestColorIndex(palette, c, pos);
|
||||
|
||||
int[] closest = closestMap.get(c);
|
||||
if (closest == null) {
|
||||
closest = new int[4];
|
||||
closest[2] = closest[3] = Integer.MAX_VALUE;
|
||||
|
||||
double pr = PR, pg = PG, pb = PB, pa = PA;
|
||||
if (palette.length < 3)
|
||||
pr = pg = pb = pa = 1;
|
||||
|
||||
for (; k < palette.length; ++k) {
|
||||
int c2 = palette[k];
|
||||
|
||||
double err = pr * BitmapUtilities.sqr(Color.red(c2) - Color.red(c));
|
||||
if (err >= closest[3])
|
||||
continue;
|
||||
|
||||
err += pg * BitmapUtilities.sqr(Color.green(c2) - Color.green(c));
|
||||
if (err >= closest[3])
|
||||
continue;
|
||||
|
||||
err += pb * BitmapUtilities.sqr(Color.blue(c2) - Color.blue(c));
|
||||
if (err >= closest[3])
|
||||
continue;
|
||||
|
||||
if (hasSemiTransparency)
|
||||
err += pa * BitmapUtilities.sqr(Color.alpha(c2) - Color.alpha(c));
|
||||
|
||||
if (err < closest[2]) {
|
||||
closest[1] = closest[0];
|
||||
closest[3] = closest[2];
|
||||
closest[0] = k;
|
||||
closest[2] = (int) err;
|
||||
} else if (err < closest[3]) {
|
||||
closest[1] = k;
|
||||
closest[3] = (int) err;
|
||||
}
|
||||
}
|
||||
|
||||
if (closest[3] == Integer.MAX_VALUE)
|
||||
closest[1] = closest[0];
|
||||
|
||||
closestMap.put(c, closest);
|
||||
}
|
||||
|
||||
int MAX_ERR = palette.length << 2;
|
||||
int idx = (pos + 1) % 2;
|
||||
if (closest[3] * .67 < (closest[3] - closest[2]))
|
||||
idx = 0;
|
||||
else if (closest[0] > closest[1])
|
||||
idx = pos % 2;
|
||||
|
||||
if (closest[idx + 2] >= MAX_ERR || (hasAlpha() && closest[idx] == 0))
|
||||
return nearestColorIndex(palette, c, pos);
|
||||
return (short) closest[idx];
|
||||
}
|
||||
|
||||
protected Ditherable getDitherFn(final boolean dither) {
|
||||
return new Ditherable() {
|
||||
@Override
|
||||
public int getColorIndex(int c) {
|
||||
return BitmapUtilities.getColorIndex(c, hasSemiTransparency, m_transparentPixelIndex >= 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short nearestColorIndex(int[] palette, int c, final int pos) {
|
||||
if (dither)
|
||||
return PnnQuantizer.this.nearestColorIndex(palette, c, pos);
|
||||
return PnnQuantizer.this.closestColorIndex(palette, c, pos);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected int[] dither(final int[] cPixels, int[] palette, int width, int height, boolean dither) {
|
||||
Ditherable ditherable = getDitherFn(dither);
|
||||
if (hasSemiTransparency)
|
||||
weight *= -1;
|
||||
int[] qPixels = GilbertCurve.dither(width, height, cPixels, palette, ditherable, null, weight, dither);
|
||||
|
||||
if (!dither && palette.length > 32)
|
||||
BlueNoise.dither(width, height, cPixels, palette, ditherable, qPixels, 1.0f);
|
||||
|
||||
closestMap.clear();
|
||||
nearestMap.clear();
|
||||
|
||||
return qPixels;
|
||||
}
|
||||
|
||||
public Pair<Bitmap, int[]> convert(int nMaxColors, boolean dither) {
|
||||
int semiTransCount = 0;
|
||||
for (int i = 0; i < pixels.length; ++i) {
|
||||
int pixel = pixels[i];
|
||||
int alfa = (pixel >> 24) & 0xff;
|
||||
int r = (pixel >> 16) & 0xff;
|
||||
int g = (pixel >> 8) & 0xff;
|
||||
int b = (pixel) & 0xff;
|
||||
pixels[i] = Color.argb(alfa, r, g, b);
|
||||
if (alfa < 0xE0) {
|
||||
if (alfa == 0) {
|
||||
m_transparentPixelIndex = i;
|
||||
if (nMaxColors > 2)
|
||||
m_transparentColor = pixels[i];
|
||||
else
|
||||
pixels[i] = m_transparentColor;
|
||||
} else if (alfa > alphaThreshold)
|
||||
++semiTransCount;
|
||||
}
|
||||
}
|
||||
|
||||
hasSemiTransparency = semiTransCount > 0;
|
||||
if (nMaxColors <= 32)
|
||||
PR = PG = PB = PA = 1;
|
||||
else {
|
||||
PR = coeffs[0][0];
|
||||
PG = coeffs[0][1];
|
||||
PB = coeffs[0][2];
|
||||
}
|
||||
|
||||
int[] palette;
|
||||
if (nMaxColors > 2)
|
||||
palette = pnnquan(pixels, nMaxColors);
|
||||
else {
|
||||
palette = new int[nMaxColors];
|
||||
weight = 1;
|
||||
if (m_transparentPixelIndex >= 0) {
|
||||
palette[0] = m_transparentColor;
|
||||
palette[1] = Color.BLACK;
|
||||
} else {
|
||||
palette[0] = Color.BLACK;
|
||||
palette[1] = Color.WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
int[] qPixels = dither(pixels, palette, width, height, dither);
|
||||
return Pair.create(
|
||||
Bitmap.createBitmap(qPixels, width, height, Bitmap.Config.ARGB_8888),
|
||||
palette
|
||||
);
|
||||
}
|
||||
|
||||
public boolean hasAlpha() {
|
||||
return m_transparentPixelIndex > -1;
|
||||
}
|
||||
protected short alphaThreshold = 0xF;
|
||||
protected boolean hasSemiTransparency = false;
|
||||
protected int m_transparentPixelIndex = -1;
|
||||
protected int width, height;
|
||||
protected int[] pixels = null;
|
||||
protected Integer m_transparentColor = Color.argb(0, BYTE_MAX, BYTE_MAX, BYTE_MAX);
|
||||
|
||||
protected double PR = 0.299, PG = 0.587, PB = 0.114, PA = .3333;
|
||||
protected double ratio = .5, weight = 1;
|
||||
protected static final float[][] coeffs = new float[][] {
|
||||
{0.299f, 0.587f, 0.114f},
|
||||
{-0.14713f, -0.28886f, 0.436f},
|
||||
{0.615f, -0.51499f, -0.10001f}
|
||||
};
|
||||
|
||||
protected Map<Integer, int[]> closestMap = new HashMap<>();
|
||||
protected Map<Integer, Short> nearestMap = new HashMap<>();
|
||||
|
||||
public PnnQuantizer(String fname) {
|
||||
fromBitmap(fname);
|
||||
}
|
||||
|
||||
private void fromBitmap(Bitmap bitmap) {
|
||||
width = bitmap.getWidth();
|
||||
height = bitmap.getHeight();
|
||||
pixels = new int [width * height];
|
||||
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
|
||||
}
|
||||
|
||||
private void fromBitmap(String fname) {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(fname);
|
||||
fromBitmap(bitmap);
|
||||
}
|
||||
|
||||
private static final class Pnnbin {
|
||||
double ac = 0, rc = 0, gc = 0, bc = 0;
|
||||
float cnt = 0, err = 0;
|
||||
int nn, fw, bk, tm, mtm;
|
||||
}
|
||||
|
||||
private void find_nn(Pnnbin[] bins, int idx)
|
||||
{
|
||||
int nn = 0;
|
||||
double err = 1e100;
|
||||
|
||||
Pnnbin bin1 = bins[idx];
|
||||
float n1 = bin1.cnt;
|
||||
double wa = bin1.ac;
|
||||
double wr = bin1.rc;
|
||||
double wg = bin1.gc;
|
||||
double wb = bin1.bc;
|
||||
|
||||
int start = 0;
|
||||
if(BlueNoise.TELL_BLUE_NOISE[idx & 4095] > 0)
|
||||
start = (PG < coeffs[0][1]) ? coeffs.length : 1;
|
||||
|
||||
for (int i = bin1.fw; i != 0; i = bins[i].fw) {
|
||||
double n2 = bins[i].cnt, nerr2 = (n1 * n2) / (n1 + n2);
|
||||
if (nerr2 >= err)
|
||||
continue;
|
||||
|
||||
double nerr = 0.0;
|
||||
if(hasSemiTransparency) {
|
||||
nerr += nerr2 * PA * BitmapUtilities.sqr(bins[i].ac - wa);
|
||||
if (nerr >= err)
|
||||
continue;
|
||||
}
|
||||
|
||||
nerr += nerr2 * (1 - ratio) * PR * BitmapUtilities.sqr(bins[i].rc - wr);
|
||||
if (nerr >= err)
|
||||
continue;
|
||||
|
||||
nerr += nerr2 * (1 - ratio) * PG * BitmapUtilities.sqr(bins[i].gc - wg);
|
||||
if (nerr >= err)
|
||||
continue;
|
||||
|
||||
nerr += nerr2 * (1 - ratio) * PB * BitmapUtilities.sqr(bins[i].bc - wb);
|
||||
if (nerr >= err)
|
||||
continue;
|
||||
|
||||
for (int j = start; j < coeffs.length; ++j) {
|
||||
nerr += nerr2 * ratio * BitmapUtilities.sqr(coeffs[j][0] * (bins[i].rc - wr));
|
||||
if (nerr >= err)
|
||||
break;
|
||||
|
||||
nerr += nerr2 * ratio * BitmapUtilities.sqr(coeffs[j][1] * (bins[i].gc - wg));
|
||||
if (nerr >= err)
|
||||
break;
|
||||
|
||||
nerr += nerr2 * ratio * BitmapUtilities.sqr(coeffs[j][2] * (bins[i].bc - wb));
|
||||
if (nerr >= err)
|
||||
break;
|
||||
}
|
||||
|
||||
err = nerr;
|
||||
nn = i;
|
||||
}
|
||||
bin1.err = (float) err;
|
||||
bin1.nn = nn;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface QuanFn {
|
||||
float get(float cnt);
|
||||
}
|
||||
|
||||
protected QuanFn getQuanFn(int nMaxColors, short quan_rt) {
|
||||
if (quan_rt > 0) {
|
||||
if (nMaxColors < 64)
|
||||
return cnt -> (float) Math.sqrt(cnt);
|
||||
return cnt -> (int) Math.sqrt(cnt);
|
||||
}
|
||||
if (quan_rt < 0)
|
||||
return cnt -> (int) Math.cbrt(cnt);
|
||||
return cnt -> cnt;
|
||||
}
|
||||
|
||||
protected Integer[] pnnquan(final int[] pixels, int nMaxColors)
|
||||
{
|
||||
short quan_rt = (short) 1;
|
||||
Pnnbin[] bins = new Pnnbin[65536];
|
||||
|
||||
/* Build histogram */
|
||||
for (int pixel : pixels) {
|
||||
if (Color.alpha(pixel) <= alphaThreshold)
|
||||
pixel = m_transparentColor;
|
||||
|
||||
int index = BitmapUtilities.getColorIndex(pixel, hasSemiTransparency, nMaxColors < 64 || m_transparentPixelIndex >= 0);
|
||||
|
||||
if(bins[index] == null)
|
||||
bins[index] = new Pnnbin();
|
||||
Pnnbin tb = bins[index];
|
||||
tb.ac += Color.alpha(pixel);
|
||||
tb.rc += Color.red(pixel);
|
||||
tb.gc += Color.green(pixel);
|
||||
tb.bc += Color.blue(pixel);
|
||||
tb.cnt++;
|
||||
}
|
||||
|
||||
/* Cluster nonempty bins at one end of array */
|
||||
int maxbins = 0;
|
||||
|
||||
for (int i = 0; i < bins.length; ++i) {
|
||||
if (bins[i] == null)
|
||||
continue;
|
||||
|
||||
float d = 1f / bins[i].cnt;
|
||||
bins[i].ac *= d;
|
||||
bins[i].rc *= d;
|
||||
bins[i].gc *= d;
|
||||
bins[i].bc *= d;
|
||||
|
||||
bins[maxbins++] = bins[i];
|
||||
}
|
||||
|
||||
if(nMaxColors < 16)
|
||||
quan_rt = -1;
|
||||
|
||||
weight = Math.min(0.9, nMaxColors * 1.0 / maxbins);
|
||||
if (weight < .04 && PG >= coeffs[0][1]) {
|
||||
PR = PG = PB = PA = 1;
|
||||
if (nMaxColors >= 64)
|
||||
quan_rt = 0;
|
||||
}
|
||||
|
||||
QuanFn quanFn = getQuanFn(nMaxColors, quan_rt);
|
||||
|
||||
int j = 0;
|
||||
for (; j < maxbins - 1; ++j) {
|
||||
bins[j].fw = j + 1;
|
||||
bins[j + 1].bk = j;
|
||||
|
||||
bins[j].cnt = quanFn.get(bins[j].cnt);
|
||||
}
|
||||
bins[j].cnt = quanFn.get(bins[j].cnt);
|
||||
|
||||
int h, l, l2;
|
||||
/* Initialize nearest neighbors and build heap of them */
|
||||
int[] heap = new int[bins.length + 1];
|
||||
for (int i = 0; i < maxbins; i++) {
|
||||
find_nn(bins, i);
|
||||
/* Push slot on heap */
|
||||
float err = bins[i].err;
|
||||
for (l = ++heap[0]; l > 1; l = l2) {
|
||||
l2 = l >> 1;
|
||||
if (bins[h = heap[l2]].err <= err)
|
||||
break;
|
||||
heap[l] = h;
|
||||
}
|
||||
heap[l] = i;
|
||||
}
|
||||
|
||||
/* Merge bins which increase error the least */
|
||||
int extbins = maxbins - nMaxColors;
|
||||
for (int i = 0; i < extbins; ) {
|
||||
Pnnbin tb;
|
||||
/* Use heap to find which bins to merge */
|
||||
for (;;) {
|
||||
int b1 = heap[1];
|
||||
tb = bins[b1]; /* One with least error */
|
||||
/* Is stored error up to date? */
|
||||
if ((tb.tm >= tb.mtm) && (bins[tb.nn].mtm <= tb.tm))
|
||||
break;
|
||||
if (tb.mtm == 0xFFFF) /* Deleted node */
|
||||
b1 = heap[1] = heap[heap[0]--];
|
||||
else /* Too old error value */
|
||||
{
|
||||
find_nn(bins, b1);
|
||||
tb.tm = i;
|
||||
}
|
||||
/* Push slot down */
|
||||
float err = bins[b1].err;
|
||||
for (l = 1; (l2 = l + l) <= heap[0]; l = l2) {
|
||||
if ((l2 < heap[0]) && (bins[heap[l2]].err > bins[heap[l2 + 1]].err))
|
||||
++l2;
|
||||
if (err <= bins[h = heap[l2]].err)
|
||||
break;
|
||||
heap[l] = h;
|
||||
}
|
||||
heap[l] = b1;
|
||||
}
|
||||
|
||||
/* Do a merge */
|
||||
Pnnbin nb = bins[tb.nn];
|
||||
float n1 = tb.cnt;
|
||||
float n2 = nb.cnt;
|
||||
float d = 1f / (n1 + n2);
|
||||
tb.ac = d * Math.round(n1 * tb.ac + n2 * nb.ac);
|
||||
tb.rc = d * Math.round(n1 * tb.rc + n2 * nb.rc);
|
||||
tb.gc = d * Math.round(n1 * tb.gc + n2 * nb.gc);
|
||||
tb.bc = d * Math.round(n1 * tb.bc + n2 * nb.bc);
|
||||
tb.cnt += n2;
|
||||
tb.mtm = ++i;
|
||||
|
||||
/* Unchain deleted bin */
|
||||
bins[nb.bk].fw = nb.fw;
|
||||
bins[nb.fw].bk = nb.bk;
|
||||
nb.mtm = 0xFFFF;
|
||||
}
|
||||
|
||||
/* Fill palette */
|
||||
Integer[] palette = new Integer[extbins > 0 ? nMaxColors : maxbins];
|
||||
short k = 0;
|
||||
for (int i = 0; k < palette.length; ++k) {
|
||||
palette[k] = Color.argb((int) bins[i].ac, (int) bins[i].rc, (int) bins[i].gc, (int) bins[i].bc);
|
||||
|
||||
i = bins[i].fw;
|
||||
}
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
||||
protected short nearestColorIndex(final Integer[] palette, int c, final int pos)
|
||||
{
|
||||
final int offset = weight > .015 ? c : BitmapUtilities.getColorIndex(c, hasSemiTransparency, m_transparentPixelIndex >= 0);
|
||||
Short got = nearestMap.get(offset);
|
||||
if (got != null)
|
||||
return got;
|
||||
|
||||
short k = 0;
|
||||
if (Color.alpha(c) <= alphaThreshold)
|
||||
c = m_transparentColor;
|
||||
if(palette.length > 2 && hasAlpha() && Color.alpha(c) > alphaThreshold)
|
||||
k = 1;
|
||||
|
||||
double pr = PR, pg = PG, pb = PB, pa = PA;
|
||||
if(palette.length < 3)
|
||||
pr = pg = pb = pa = 1;
|
||||
|
||||
double mindist = Integer.MAX_VALUE;
|
||||
for (short i=k; i<palette.length; ++i) {
|
||||
int c2 = palette[i];
|
||||
|
||||
double curdist = pa * BitmapUtilities.sqr(Color.alpha(c2) - Color.alpha(c));
|
||||
if (curdist > mindist)
|
||||
continue;
|
||||
|
||||
curdist += pr * BitmapUtilities.sqr(Color.red(c2) - Color.red(c));
|
||||
if (curdist > mindist)
|
||||
continue;
|
||||
|
||||
curdist += pg * BitmapUtilities.sqr(Color.green(c2) - Color.green(c));
|
||||
if (curdist > mindist)
|
||||
continue;
|
||||
|
||||
curdist += pb * BitmapUtilities.sqr(Color.blue(c2) - Color.blue(c));
|
||||
if (curdist > mindist)
|
||||
continue;
|
||||
|
||||
mindist = curdist;
|
||||
k = i;
|
||||
}
|
||||
nearestMap.put(offset, k);
|
||||
return k;
|
||||
}
|
||||
|
||||
protected short closestColorIndex(final Integer[] palette, int c, final int pos)
|
||||
{
|
||||
short k = 0;
|
||||
if (Color.alpha(c) <= alphaThreshold)
|
||||
return nearestColorIndex(palette, c, pos);
|
||||
|
||||
final int offset = weight > .015 ? c : BitmapUtilities.getColorIndex(c, hasSemiTransparency, m_transparentPixelIndex >= 0);
|
||||
int[] closest = closestMap.get(c);
|
||||
if (closest == null) {
|
||||
closest = new int[4];
|
||||
closest[2] = closest[3] = Integer.MAX_VALUE;
|
||||
|
||||
double pr = PR, pg = PG, pb = PB, pa = PA;
|
||||
if(palette.length < 3)
|
||||
pr = pg = pb = pa = 1;
|
||||
|
||||
for (; k < palette.length; ++k) {
|
||||
int c2 = palette[k];
|
||||
|
||||
double err = pr * BitmapUtilities.sqr(Color.red(c2) - Color.red(c));
|
||||
if (err >= closest[3])
|
||||
continue;
|
||||
|
||||
err += pg * BitmapUtilities.sqr(Color.green(c2) - Color.green(c));
|
||||
if (err >= closest[3])
|
||||
continue;
|
||||
|
||||
err += pb * BitmapUtilities.sqr(Color.blue(c2) - Color.blue(c));
|
||||
if (err >= closest[3])
|
||||
continue;
|
||||
|
||||
if (hasSemiTransparency)
|
||||
err += pa * BitmapUtilities.sqr(Color.alpha(c2) - Color.alpha(c));
|
||||
|
||||
if (err < closest[2]) {
|
||||
closest[1] = closest[0];
|
||||
closest[3] = closest[2];
|
||||
closest[0] = k;
|
||||
closest[2] = (int) err;
|
||||
}
|
||||
else if (err < closest[3]) {
|
||||
closest[1] = k;
|
||||
closest[3] = (int) err;
|
||||
}
|
||||
}
|
||||
|
||||
if (closest[3] == Integer.MAX_VALUE)
|
||||
closest[1] = closest[0];
|
||||
|
||||
closestMap.put(offset, closest);
|
||||
}
|
||||
|
||||
int MAX_ERR = palette.length << 2;
|
||||
int idx = (pos + 1) % 2;
|
||||
if (closest[3] * .67 < (closest[3] - closest[2]))
|
||||
idx = 0;
|
||||
else if (closest[0] > closest[1])
|
||||
idx = pos % 2;
|
||||
|
||||
if(closest[idx + 2] >= MAX_ERR || (hasAlpha() && closest[idx] == 0))
|
||||
return nearestColorIndex(palette, c, pos);
|
||||
return (short) closest[idx];
|
||||
}
|
||||
|
||||
protected Ditherable getDitherFn(final boolean dither) {
|
||||
return new Ditherable() {
|
||||
@Override
|
||||
public int getColorIndex(int c) {
|
||||
return BitmapUtilities.getColorIndex(c, hasSemiTransparency, m_transparentPixelIndex >= 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short nearestColorIndex(Integer[] palette, int c, final int pos) {
|
||||
if(dither)
|
||||
return PnnQuantizer.this.nearestColorIndex(palette, c, pos);
|
||||
return PnnQuantizer.this.closestColorIndex(palette, c, pos);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected int[] dither(final int[] cPixels, Integer[] palette, int width, int height, boolean dither) throws Exception
|
||||
{
|
||||
Ditherable ditherable = getDitherFn(dither);
|
||||
if(hasSemiTransparency)
|
||||
weight *= -1;
|
||||
int[] qPixels = GilbertCurve.dither(width, height, cPixels, palette, ditherable, null, weight, dither);
|
||||
|
||||
if (!dither && palette.length > 32)
|
||||
BlueNoise.dither(width, height, cPixels, palette, ditherable, qPixels, 1.0f);
|
||||
|
||||
closestMap.clear();
|
||||
nearestMap.clear();
|
||||
|
||||
return qPixels;
|
||||
}
|
||||
|
||||
public Bitmap convert(int nMaxColors, boolean dither) throws Exception {
|
||||
int semiTransCount = 0;
|
||||
for (int i = 0; i < pixels.length; ++i) {
|
||||
int pixel = pixels[i];
|
||||
int alfa = (pixel >> 24) & 0xff;
|
||||
int r = (pixel >> 16) & 0xff;
|
||||
int g = (pixel >> 8) & 0xff;
|
||||
int b = (pixel ) & 0xff;
|
||||
pixels[i] = Color.argb(alfa, r, g, b);
|
||||
if (alfa < 0xE0) {
|
||||
if (alfa == 0) {
|
||||
m_transparentPixelIndex = i;
|
||||
if(nMaxColors > 2)
|
||||
m_transparentColor = pixels[i];
|
||||
else
|
||||
pixels[i] = m_transparentColor;
|
||||
}
|
||||
else if (alfa > alphaThreshold)
|
||||
++semiTransCount;
|
||||
}
|
||||
}
|
||||
|
||||
hasSemiTransparency = semiTransCount > 0;
|
||||
if (nMaxColors <= 32)
|
||||
PR = PG = PB = PA = 1;
|
||||
else {
|
||||
PR = coeffs[0][0]; PG = coeffs[0][1]; PB = coeffs[0][2];
|
||||
}
|
||||
|
||||
Integer[] palette;
|
||||
if (nMaxColors > 2)
|
||||
palette = pnnquan(pixels, nMaxColors);
|
||||
else {
|
||||
palette = new Integer[nMaxColors];
|
||||
weight = 1;
|
||||
if (m_transparentPixelIndex >= 0) {
|
||||
palette[0] = m_transparentColor;
|
||||
palette[1] = Color.BLACK;
|
||||
}
|
||||
else {
|
||||
palette[0] = Color.BLACK;
|
||||
palette[1] = Color.WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
int[] qPixels = dither(pixels, palette, width, height, dither);
|
||||
return Bitmap.createBitmap(qPixels, width, height, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
public boolean hasAlpha() {
|
||||
return m_transparentPixelIndex > -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user