nQuant: backport Gadgetbridge fixups

This commit is contained in:
Thomas Kuehne
2026-03-09 22:28:03 +01:00
committed by José Rebelo
parent c8da8b4717
commit 07b1e593b3
7 changed files with 39 additions and 27 deletions
@@ -37,7 +37,7 @@ public class BitmapUtilities {
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)
static int[] quantize_image(final int width, final int height, final int[] pixels, final int[] palette, final Ditherable ditherable, final boolean hasSemiTransparency, final boolean dither)
{
int[] qPixels = new int[pixels.length];
int nMaxColors = palette.length;
@@ -196,7 +196,7 @@ public class BlueNoise {
return Color.argb(a_pix, r_pix, g_pix, b_pix);
}
static int[] processImagePixels(final Integer[] palette, final int[] qPixels) {
static int[] processImagePixels(final int[] palette, final int[] qPixels) {
int[] qPixel32s = new int[qPixels.length];
for (var i = 0; i < qPixels.length; ++i)
qPixel32s[i] = palette[qPixels[i]];
@@ -204,7 +204,7 @@ public class BlueNoise {
return qPixel32s;
}
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)
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) {
@@ -3,5 +3,5 @@ package com.android.nQuant;
public interface Ditherable {
public int getColorIndex(final int c);
public short nearestColorIndex(final Integer[] palette, final int c, final int pos);
public short nearestColorIndex(final int[] palette, final int c, final int pos);
}
@@ -38,7 +38,7 @@ public class GilbertCurve {
private final int width, height;
private final double weight;
private final int[] pixels;
private final Integer[] palette;
private final int[] palette;
private final int[] qPixels;
private final Ditherable ditherable;
private final float[] saliencies;
@@ -47,7 +47,7 @@ public class GilbertCurve {
private final int margin, thresold;
private static final float BLOCK_SIZE = 343f;
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)
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;
@@ -279,7 +279,7 @@ public class GilbertCurve {
qPixels[bidx] = palette[qPixels[bidx]];
}
private void generate2d(int x, int y, int ax, int ay, int bx, int by) throws Exception {
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);
@@ -353,7 +353,7 @@ public class GilbertCurve {
weights[0] += 1f - weight;
}
private void run() throws Exception
private void run()
{
if(!sortedByYDiff)
initWeights(DITHER_MAX);
@@ -364,7 +364,7 @@ public class GilbertCurve {
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
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)
{
int[] qPixels = new int[pixels.length];
new GilbertCurve(width, height, pixels, palette, qPixels, ditherable, saliencies, weight, dither).run();
@@ -4,6 +4,7 @@ package com.android.nQuant;
Copyright (c) 2018-2026 Miller Cy Chan
* error measure; time used is proportional to number of bins squared - WJ */
import android.graphics.Bitmap;
import android.graphics.Color;
import com.android.nQuant.CIELABConvertor.Lab;
@@ -25,6 +26,10 @@ public class PnnLABQuantizer extends PnnQuantizer {
super(fname);
}
public PnnLABQuantizer(Bitmap bitmap) {
super(bitmap);
}
private static final class Pnnbin {
float ac = 0, Lc = 0, Ac = 0, Bc = 0, err = 0;
float cnt = 0;
@@ -128,7 +133,7 @@ public class PnnLABQuantizer extends PnnQuantizer {
}
@Override
protected Integer[] pnnquan(final int[] pixels, int nMaxColors)
protected int[] pnnquan(final int[] pixels, int nMaxColors)
{
short quan_rt = (short) 1;
Pnnbin[] bins = new Pnnbin[65536];
@@ -192,7 +197,7 @@ public class PnnLABQuantizer extends PnnQuantizer {
if(pixelMap.size() <= nMaxColors) {
/* Fill palette */
Integer[] palette = new Integer[pixelMap.size()];
int[] palette = new int[pixelMap.size()];
int k = 0;
for (Integer pixel : pixelMap.keySet()) {
palette[k++] = pixel;
@@ -312,7 +317,7 @@ public class PnnLABQuantizer extends PnnQuantizer {
}
/* Fill palette */
Integer[] palette = new Integer[extbins > 0 ? nMaxColors : maxbins];
int[] palette = new int[extbins > 0 ? nMaxColors : maxbins];
short k = 0;
for (int i = 0; k < palette.length; ++k) {
Lab lab1 = new Lab();
@@ -327,7 +332,7 @@ public class PnnLABQuantizer extends PnnQuantizer {
}
@Override
protected short nearestColorIndex(final Integer[] palette, int c, final int pos)
protected short nearestColorIndex(final int[] palette, int c, final int pos)
{
final int offset = !isNano ? c : BitmapUtilities.getColorIndex(c, hasSemiTransparency, m_transparentPixelIndex >= 0);
Short got = nearestMap.get(offset);
@@ -404,7 +409,7 @@ public class PnnLABQuantizer extends PnnQuantizer {
}
@Override
protected short closestColorIndex(final Integer[] palette, int c, final int pos)
protected short closestColorIndex(final int[] palette, int c, final int pos)
{
if (Color.alpha(c) <= alphaThreshold)
return nearestColorIndex(palette, c, pos);
@@ -481,7 +486,7 @@ public class PnnLABQuantizer extends PnnQuantizer {
}
@Override
public short nearestColorIndex(Integer[] palette, int c, final int pos) {
public short nearestColorIndex(int[] palette, int c, final int pos) {
if (palette.length <= 4)
return PnnLABQuantizer.this.nearestColorIndex(palette, c, pos);
return PnnLABQuantizer.this.closestColorIndex(palette, c, pos);
@@ -490,7 +495,7 @@ public class PnnLABQuantizer extends PnnQuantizer {
}
@Override
protected int[] dither(final int[] cPixels, Integer[] palette, int width, int height, boolean dither) throws Exception
public int[] dither(final int[] cPixels, int[] palette, int width, int height, boolean dither)
{
Ditherable ditherable = getDitherFn();
if(hasSemiTransparency)
@@ -7,6 +7,7 @@ Copyright (c) 2018-2026 Miller Cy Chan
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.util.Pair;
import java.util.HashMap;
import java.util.Map;
@@ -36,6 +37,10 @@ public class PnnQuantizer {
fromBitmap(fname);
}
public PnnQuantizer(Bitmap bitmap) {
fromBitmap(bitmap);
}
private void fromBitmap(Bitmap bitmap) {
width = bitmap.getWidth();
height = bitmap.getHeight();
@@ -131,7 +136,7 @@ public class PnnQuantizer {
return cnt -> cnt;
}
protected Integer[] pnnquan(final int[] pixels, int nMaxColors)
protected int[] pnnquan(final int[] pixels, int nMaxColors)
{
short quan_rt = (short) 1;
Pnnbin[] bins = new Pnnbin[65536];
@@ -255,7 +260,7 @@ public class PnnQuantizer {
}
/* Fill palette */
Integer[] palette = new Integer[extbins > 0 ? nMaxColors : maxbins];
int[] palette = new int[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);
@@ -266,7 +271,7 @@ public class PnnQuantizer {
return palette;
}
protected short nearestColorIndex(final Integer[] palette, int c, final int pos)
protected short nearestColorIndex(final int[] palette, int c, final int pos)
{
final int offset = weight > .015 ? c : BitmapUtilities.getColorIndex(c, hasSemiTransparency, m_transparentPixelIndex >= 0);
Short got = nearestMap.get(offset);
@@ -310,7 +315,7 @@ public class PnnQuantizer {
return k;
}
protected short closestColorIndex(final Integer[] palette, int c, final int pos)
protected short closestColorIndex(final int[] palette, int c, final int pos)
{
short k = 0;
if (Color.alpha(c) <= alphaThreshold)
@@ -382,7 +387,7 @@ public class PnnQuantizer {
}
@Override
public short nearestColorIndex(Integer[] palette, int c, final int pos) {
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);
@@ -390,7 +395,7 @@ public class PnnQuantizer {
};
}
protected int[] dither(final int[] cPixels, Integer[] palette, int width, int height, boolean dither) throws Exception
protected int[] dither(final int[] cPixels, int[] palette, int width, int height, boolean dither)
{
Ditherable ditherable = getDitherFn(dither);
if(hasSemiTransparency)
@@ -406,7 +411,7 @@ public class PnnQuantizer {
return qPixels;
}
public Bitmap convert(int nMaxColors, boolean dither) throws Exception {
public Pair<Bitmap, int[]> convert(int nMaxColors, boolean dither) {
int semiTransCount = 0;
for (int i = 0; i < pixels.length; ++i) {
int pixel = pixels[i];
@@ -435,11 +440,11 @@ public class PnnQuantizer {
PR = coeffs[0][0]; PG = coeffs[0][1]; PB = coeffs[0][2];
}
Integer[] palette;
int[] palette;
if (nMaxColors > 2)
palette = pnnquan(pixels, nMaxColors);
else {
palette = new Integer[nMaxColors];
palette = new int[nMaxColors];
weight = 1;
if (m_transparentPixelIndex >= 0) {
palette[0] = m_transparentColor;
@@ -452,7 +457,9 @@ public class PnnQuantizer {
}
int[] qPixels = dither(pixels, palette, width, height, dither);
return Bitmap.createBitmap(qPixels, width, height, Bitmap.Config.ARGB_8888);
return new Pair<>(
Bitmap.createBitmap(qPixels, width, height, Bitmap.Config.ARGB_8888),
palette);
}
public boolean hasAlpha() {
@@ -1,6 +1,6 @@
/**
* All files under this package were adapted from https://github.com/mcychan/nQuant.android
* at 8f18e9d7536e71a90d0d1333968e07688b7ebf09
* at 12822f15c92695136f1761b6b72c9bd18dc70c46
* <p>
* Changes done:
* - Update PnnQuantizer to allow for access to the palette after convertion