I. Floyd-Steinberg-Dithering
Github: Native-Floyd-Steinberg-Dithering
该作者对Floyd-Steinberg-Dithering 的处理中,使用的是静态数组,容易栈溢出。
这里,给出两种实现来避免。当然,若一次处理的位图信息过大,也是会堆溢出,OOM的。
I.i. 动态数组实现
cpp 代码如下。
void floydSteinberg1(AndroidBitmapInfo *info, void *pixels) {
uint32_t *pixelLine;
void *p = pixels;
int w = info->width;
int h = info->height;
// uint32_t ary[h][w];
uint32_t **ary = new uint32_t *[h];
for (int y = 0; y < h; ++y) {
ary[y] = new uint32_t[w];
pixelLine = (uint32_t *) p;
for (int x = 0; x < w; ++x) {
//extract the RGB values from the pixel
ary[y][x] = 0xFF000000 | (pixelLine[x] & 0x00FFFFFF);
}
p = (char *) p + info->stride;
}
for (int y = 0; y < h; ++y) {
pixelLine = (uint32_t *) pixels;
for (int x = 0; x < w; ++x) {
uint32_t oldPixel = ary[y][x];
// uint32_t newPixel = find_closest_palette_color(oldPixel);
uint32_t newPixel = (int) oldPixel >= 128 ? 0xFFFFFFFF : 0xFF000000;
pixelLine[x] = newPixel;
uint32_t err = oldPixel - newPixel;
if (x + 1 < w)
ary[y][x + 1] += (int) (err * (7. / 16));
if (x > 0 && y + 1 < h)
ary[y + 1][x - 1] += (int) (err * (3. / 16));
if (y + 1 < h)
ary[y + 1][x] += (int) (err * (5. / 16));
if (x + 1 < w && y + 1 < h)
ary[y + 1][x + 1] += (int) (err * (1. / 16));
}
pixels = (char *) pixels + info->stride;
}
for (int y = 0; y < h; ++y) {
delete ary[y];
}
delete[] ary;
}
I.ii. 指针实现
void floydSteinberg2(AndroidBitmapInfo *info, void *pixels) {
int w = info->width;
int h = info->height;
int mid = 128;
uint32_t min = 0xFF000000;
uint32_t max = 0xFFFFFFFF;
// uint32_t minWhite = 0xFFFF0000;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
uint32_t pixel = *((uint32_t *) pixels + y * w + x);
// if (pixel >= minWhite && pixel <= max) {//接近白色不处理
// continue;
// }
uint32_t err = (int) pixel >= mid ? pixel - max : pixel - min;
if (x + 1 < w) {
*((uint32_t *) pixels + y * w + (x + 1)) += (int) (err * (7. / 16));
}
if (x >= 1 && y + 1 < h) {
*((uint32_t *) pixels + (y + 1) * w + (x - 1)) += (int) (err * (3. / 16));
}
if (y + 1 < h) {
*((uint32_t *) pixels + (y + 1) * w + x) += (int) (err * (5. / 16));
}
if (x + 1 < w && y + 1 < h) {
*((uint32_t *) pixels + (y + 1) * w + (x + 1)) += (int) (err * (1. / 16));
}
pixel = (int) pixel >= mid ? max : min;
*((uint32_t *) pixels + y * w + x) = pixel;
}
}
}
II. Stucki
本算法,是从一段c#代码中移植出来的。
void stucki(AndroidBitmapInfo *info, void *pixels) {
int w = info->width;
int h = info->height;
int mid = 128;
uint32_t max = 0xFF000000;
uint32_t min = 0xFFFFFFFF;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
uint32_t pixel = *((uint32_t *) pixels + y * w + x);
uint32_t err = (int) pixel >= mid ? pixel - min : pixel - max;
if (x + 1 < w)
*((uint32_t *) pixels + y * w + (x + 1)) += (int) (err * (8. / 42));
if (x + 2 < w)
*((uint32_t *) pixels + y * w + (x + 2)) += (int) (err * (4. / 42));
if (x >= 2 && y + 1 < h)
*((uint32_t *) pixels + (y + 1) * w + (x - 2)) += (int) (err * (2. / 42));
if (x >= 1 && y + 1 < h)
*((uint32_t *) pixels + (y + 1) * w + (x - 1)) += (int) (err * (4. / 42));
if (y + 1 < h)
*((uint32_t *) pixels + (y + 1) * w + (x)) += (int) (err * (8. / 42));
if (x + 1 < w && y + 1 < h)
*((uint32_t *) pixels + (y + 1) * w + (x + 1)) += (int) (err * (4. / 42));
if (x + 2 < w && y + 1 < h)
*((uint32_t *) pixels + (y + 1) * w + (x + 2)) += (int) (err * (2. / 42));
if (x >= 2 && y + 2 < h)
*((uint32_t *) pixels + (y + 2) * w + (x - 2)) += (int) (err * (1. / 42));
if (x >= 1 && y + 2 < h)
*((uint32_t *) pixels + (y + 2) * w + (x - 1)) += (int) (err * (2. / 42));
if (y + 2 < h)
*((uint32_t *) pixels + (y + 2) * w + (x)) += (int) (err * (4. / 42));
if (x + 1 < w && y + 2 < h)
*((uint32_t *) pixels + (y + 2) * w + (x + 1)) += (int) (err * (2. / 42));
if (x + 2 < w && y + 2 < h)
*((uint32_t *) pixels + (y + 2) * w + (x + 2)) += (int) (err * (1. / 42));
pixel = (int) pixel >= mid ? min : max;
*((uint32_t *) pixels + y * w + x) = pixel;
}
}
}