package de.syss.NfcTool;

import android.graphics.Bitmap;

public class DitheringUtils {

    // 使用传统的 32位整型 ARGB 定义调色板：[黑色, 红色, 白色]
    // Android 中：0xFF000000(黑), 0xFFFF0000(红), 0xFFFFFFFF(白)
    private static final int[] PALETTE = {
        0xFF000000, // 黑色
        0xFFFF0000, // 红色
        0xFFFFFFFF  // 白色
    };

    public static Bitmap dithering(Bitmap image) {
        int width = image.getWidth();
        int height = image.getHeight();

        // 1. 复制一个可变的 Bitmap 用于输出结果
        Bitmap ret = image.copy(Bitmap.Config.ARGB_8888, true);

        // 2. 将所有像素一次性读入一维整型数组中，避免循环内部调用 getPixel 导致性能低下
        int[] pixels = new int[width * height];
        image.getPixels(pixels, 0, width, 0, 0, width, height);

        // 创建三个并行的普通 float 数组作为误差缓冲区，代替原有的 Color[][] 对象矩阵
        float[] rBuffer = new float[width * height];
        float[] gBuffer = new float[width * height];
        float[] bBuffer = new float[width * height];

        // 初始化缓冲区
        for (int i = 0; i < pixels.length; i++) {
            int color = pixels[i];
            rBuffer[i] = ((color >> 16) & 0xFF) / 255.0f;
            gBuffer[i] = ((color >> 8) & 0xFF) / 255.0f;
            bBuffer[i] = (color & 0xFF) / 255.0f;
        }

        // 3. Floyd-Steinberg 抖动核心算法核心循环
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int currIdx = y * width + x;

                float r = rBuffer[currIdx];
                float g = gBuffer[currIdx];
                float b = bBuffer[currIdx];

                // 寻找最接近的调色板颜色
                int closestColor = findClosestPaletteColor(r, g, b);

                // 将结果写回输出 Bitmap
                ret.setPixel(x, y, closestColor);

                // 计算误差 (旧值 - 新值)
                float targetR = ((closestColor >> 16) & 0xFF) / 255.0f;
                float targetG = ((closestColor >> 8) & 0xFF) / 255.0f;
                float targetB = (closestColor & 0xFF) / 255.0f;

                float errR = r - targetR;
                float errG = g - targetG;
                float errB = b - targetB;

                // 分散误差到周边像素 (Floyd-Steinberg 矩阵)
                // x + 1, y
                if (x + 1 < width) {
                    int idx = y * width + (x + 1);
                    rBuffer[idx] += errR * 7.0 / 16.0;
                    gBuffer[idx] += errG * 7.0 / 16.0;
                    bBuffer[idx] += errB * 7.0 / 16.0;
                }
                // x - 1, y + 1
                if (x - 1 >= 0 && y + 1 < height) {
                    int idx = (y + 1) * width + (x - 1);
                    rBuffer[idx] += errR * 3.0 / 16.0;
                    gBuffer[idx] += errG * 3.0 / 16.0;
                    bBuffer[idx] += errB * 3.0 / 16.0;
                }
                // x, y + 1
                if (y + 1 < height) {
                    int idx = (y + 1) * width + x;
                    rBuffer[idx] += errR * 5.0 / 16.0;
                    gBuffer[idx] += errG * 5.0 / 16.0;
                    bBuffer[idx] += errB * 5.0 / 16.0;
                }
                // x + 1, y + 1
                if (x + 1 < width && y + 1 < height) {
                    int idx = (y + 1) * width + (x + 1);
                    rBuffer[idx] += errR * 1.0 / 16.0;
                    gBuffer[idx] += errG * 1.0 / 16.0;
                    bBuffer[idx] += errB * 1.0 / 16.0;
                }
            }
        }

        return ret;
    }

    private static int findClosestPaletteColor(float r, float g, float b) {
        int closestColor = PALETTE[0];
        float minDistance = Float.MAX_VALUE;

        for (int color : PALETTE) {
            float pR = ((color >> 16) & 0xFF) / 255.0f;
            float pG = ((color >> 8) & 0xFF) / 255.0f;
            float pB = (color & 0xFF) / 255.0f;

            float rd = pR - r;
            float gd = pG - g;
            float bd = pB - b;
            float distance = rd * rd + gd * gd + bd * bd;

            if (distance < minDistance) {
                minDistance = distance;
                closestColor = color;
            }
        }
        return closestColor;
    }
}
