forked from aseprite/aseprite
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patherror_diffusion.cpp
117 lines (99 loc) · 2.7 KB
/
error_diffusion.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Aseprite Render Library
// Copyright (c) 2019-2022 Igara Studio S.A
// Copyright (c) 2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "render/error_diffusion.h"
#include "gfx/hsl.h"
#include "gfx/rgb.h"
#include <algorithm>
namespace render {
ErrorDiffusionDither::ErrorDiffusionDither(int transparentIndex)
: m_transparentIndex(transparentIndex)
{
}
void ErrorDiffusionDither::start(
const doc::Image* srcImage,
doc::Image* dstImage,
const double factor)
{
m_srcImage = srcImage;
m_width = 2+srcImage->width();
for (int i=0; i<kChannels; ++i)
m_err[i].resize(m_width*2, 0);
m_lastY = -1;
m_factor = int(factor * 100.0);
}
void ErrorDiffusionDither::finish()
{
}
doc::color_t ErrorDiffusionDither::ditherRgbToIndex2D(
const int x, const int y,
const doc::RgbMap* rgbmap,
const doc::Palette* palette)
{
if (y != m_lastY) {
for (int i=0; i<kChannels; ++i) {
int* row0 = &m_err[i][0];
int* row1 = row0 + m_width;
int* end1 = row1 + m_width;
std::copy(row1, end1, row0);
std::fill(row1, end1, 0);
}
m_lastY = y;
}
doc::color_t color =
doc::get_pixel_fast<doc::RgbTraits>(m_srcImage, x, y);
// Get RGB values + quatization error
int v[kChannels] = {
doc::rgba_getr(color),
doc::rgba_getg(color),
doc::rgba_getb(color),
doc::rgba_geta(color)
};
for (int i=0; i<kChannels; ++i) {
v[i] += m_err[i][x+1];
v[i] = std::clamp(v[i], 0, 255);
}
const doc::color_t index =
(rgbmap ? rgbmap->mapColor(v[0], v[1], v[2], v[3]):
palette->findBestfit(v[0], v[1], v[2], v[3], m_transparentIndex));
doc::color_t palColor = palette->getEntry(index);
if (m_transparentIndex == index || doc::rgba_geta(palColor) == 0) {
// "color" without alpha
palColor = (color & doc::rgba_rgb_mask);
}
const int quantError[kChannels] = {
v[0] - doc::rgba_getr(palColor),
v[1] - doc::rgba_getg(palColor),
v[2] - doc::rgba_getb(palColor),
v[3] - doc::rgba_geta(palColor)
};
// TODO using Floyd-Steinberg matrix here but it should be configurable
for (int i=0; i<kChannels; ++i) {
int* err = &m_err[i][x];
const int q = quantError[i] * m_factor / 100;
const int a = q * 7 / 16;
const int b = q * 3 / 16;
const int c = q * 5 / 16;
const int d = q * 1 / 16;
if (y & 1) {
err[0 ] += a;
err[m_width+2] += b;
err[m_width+1] += c;
err[m_width ] += d;
}
else {
err[ +2] += a;
err[m_width ] += b;
err[m_width+1] += c;
err[m_width+2] += d;
}
}
return index;
}
} // namespace render