1 | // Aseprite |
2 | // Copyright (C) 2020 Igara Studio S.A. |
3 | // Copyright (C) 2001-2017 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "filters/median_filter.h" |
13 | |
14 | #include "base/memory.h" |
15 | #include "doc/image_impl.h" |
16 | #include "doc/palette.h" |
17 | #include "doc/rgbmap.h" |
18 | #include "filters/filter_indexed_data.h" |
19 | #include "filters/filter_manager.h" |
20 | #include "filters/neighboring_pixels.h" |
21 | #include "filters/tiled_mode.h" |
22 | |
23 | #include <algorithm> |
24 | |
25 | namespace filters { |
26 | |
27 | using namespace doc; |
28 | |
29 | namespace { |
30 | struct GetPixelsDelegateRgba { |
31 | std::vector<std::vector<uint8_t> >& channel; |
32 | int c; |
33 | |
34 | GetPixelsDelegateRgba(std::vector<std::vector<uint8_t> >& channel) : channel(channel) { } |
35 | |
36 | void reset() { c = 0; } |
37 | |
38 | void operator()(RgbTraits::pixel_t color) |
39 | { |
40 | channel[0][c] = rgba_getr(color); |
41 | channel[1][c] = rgba_getg(color); |
42 | channel[2][c] = rgba_getb(color); |
43 | channel[3][c] = rgba_geta(color); |
44 | c++; |
45 | } |
46 | }; |
47 | |
48 | struct GetPixelsDelegateGrayscale { |
49 | std::vector<std::vector<uint8_t> >& channel; |
50 | int c; |
51 | |
52 | GetPixelsDelegateGrayscale(std::vector<std::vector<uint8_t> >& channel) : channel(channel) { } |
53 | |
54 | void reset() { c = 0; } |
55 | |
56 | void operator()(GrayscaleTraits::pixel_t color) |
57 | { |
58 | channel[0][c] = graya_getv(color); |
59 | channel[1][c] = graya_geta(color); |
60 | c++; |
61 | } |
62 | }; |
63 | |
64 | struct GetPixelsDelegateIndexed { |
65 | const Palette* pal; |
66 | std::vector<std::vector<uint8_t> >& channel; |
67 | Target target; |
68 | int c; |
69 | |
70 | GetPixelsDelegateIndexed(const Palette* pal, std::vector<std::vector<uint8_t> >& channel, Target target) |
71 | : pal(pal), channel(channel), target(target) { } |
72 | |
73 | void reset() { c = 0; } |
74 | |
75 | void operator()(IndexedTraits::pixel_t color) |
76 | { |
77 | if (target & TARGET_INDEX_CHANNEL) { |
78 | channel[0][c] = color; |
79 | } |
80 | else { |
81 | color_t rgb = pal->getEntry(color); |
82 | channel[0][c] = rgba_getr(rgb); |
83 | channel[1][c] = rgba_getg(rgb); |
84 | channel[2][c] = rgba_getb(rgb); |
85 | channel[3][c] = rgba_geta(rgb); |
86 | } |
87 | c++; |
88 | } |
89 | }; |
90 | }; |
91 | |
92 | MedianFilter::MedianFilter() |
93 | : m_tiledMode(TiledMode::NONE) |
94 | , m_width(1) |
95 | , m_height(1) |
96 | , m_ncolors(0) |
97 | , m_channel(4) |
98 | { |
99 | } |
100 | |
101 | void MedianFilter::setTiledMode(TiledMode tiled) |
102 | { |
103 | m_tiledMode = tiled; |
104 | } |
105 | |
106 | void MedianFilter::setSize(int width, int height) |
107 | { |
108 | ASSERT(width >= 1); |
109 | ASSERT(height >= 1); |
110 | |
111 | m_width = std::max(1, width); |
112 | m_height = std::max(1, height); |
113 | m_ncolors = width*height; |
114 | |
115 | for (int c = 0; c < 4; ++c) |
116 | m_channel[c].resize(m_ncolors); |
117 | } |
118 | |
119 | const char* MedianFilter::getName() |
120 | { |
121 | return "Median Blur" ; |
122 | } |
123 | |
124 | void MedianFilter::applyToRgba(FilterManager* filterMgr) |
125 | { |
126 | const Image* src = filterMgr->getSourceImage(); |
127 | uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress(); |
128 | Target target = filterMgr->getTarget(); |
129 | int color; |
130 | int r, g, b, a; |
131 | GetPixelsDelegateRgba delegate(m_channel); |
132 | int x = filterMgr->x(); |
133 | int x2 = x+filterMgr->getWidth(); |
134 | int y = filterMgr->y(); |
135 | |
136 | for (; x<x2; ++x) { |
137 | // Avoid the non-selected region |
138 | if (filterMgr->skipPixel()) { |
139 | ++dst_address; |
140 | continue; |
141 | } |
142 | |
143 | delegate.reset(); |
144 | get_neighboring_pixels<RgbTraits>(src, x, y, m_width, m_height, m_width/2, m_height/2, |
145 | m_tiledMode, delegate); |
146 | |
147 | color = get_pixel_fast<RgbTraits>(src, x, y); |
148 | |
149 | if (target & TARGET_RED_CHANNEL) { |
150 | std::sort(m_channel[0].begin(), m_channel[0].end()); |
151 | r = m_channel[0][m_ncolors/2]; |
152 | } |
153 | else |
154 | r = rgba_getr(color); |
155 | |
156 | if (target & TARGET_GREEN_CHANNEL) { |
157 | std::sort(m_channel[1].begin(), m_channel[1].end()); |
158 | g = m_channel[1][m_ncolors/2]; |
159 | } |
160 | else |
161 | g = rgba_getg(color); |
162 | |
163 | if (target & TARGET_BLUE_CHANNEL) { |
164 | std::sort(m_channel[2].begin(), m_channel[2].end()); |
165 | b = m_channel[2][m_ncolors/2]; |
166 | } |
167 | else |
168 | b = rgba_getb(color); |
169 | |
170 | if (target & TARGET_ALPHA_CHANNEL) { |
171 | std::sort(m_channel[3].begin(), m_channel[3].end()); |
172 | a = m_channel[3][m_ncolors/2]; |
173 | } |
174 | else |
175 | a = rgba_geta(color); |
176 | |
177 | *(dst_address++) = rgba(r, g, b, a); |
178 | } |
179 | } |
180 | |
181 | void MedianFilter::applyToGrayscale(FilterManager* filterMgr) |
182 | { |
183 | const Image* src = filterMgr->getSourceImage(); |
184 | uint16_t* dst_address = (uint16_t*)filterMgr->getDestinationAddress(); |
185 | Target target = filterMgr->getTarget(); |
186 | int color, k, a; |
187 | GetPixelsDelegateGrayscale delegate(m_channel); |
188 | int x = filterMgr->x(); |
189 | int x2 = x+filterMgr->getWidth(); |
190 | int y = filterMgr->y(); |
191 | |
192 | for (; x<x2; ++x) { |
193 | // Avoid the non-selected region |
194 | if (filterMgr->skipPixel()) { |
195 | ++dst_address; |
196 | continue; |
197 | } |
198 | |
199 | delegate.reset(); |
200 | get_neighboring_pixels<GrayscaleTraits>(src, x, y, m_width, m_height, m_width/2, m_height/2, |
201 | m_tiledMode, delegate); |
202 | |
203 | color = get_pixel_fast<GrayscaleTraits>(src, x, y); |
204 | |
205 | if (target & TARGET_GRAY_CHANNEL) { |
206 | std::sort(m_channel[0].begin(), m_channel[0].end()); |
207 | k = m_channel[0][m_ncolors/2]; |
208 | } |
209 | else |
210 | k = graya_getv(color); |
211 | |
212 | if (target & TARGET_ALPHA_CHANNEL) { |
213 | std::sort(m_channel[1].begin(), m_channel[1].end()); |
214 | a = m_channel[1][m_ncolors/2]; |
215 | } |
216 | else |
217 | a = graya_geta(color); |
218 | |
219 | *(dst_address++) = graya(k, a); |
220 | } |
221 | } |
222 | |
223 | void MedianFilter::applyToIndexed(FilterManager* filterMgr) |
224 | { |
225 | const Image* src = filterMgr->getSourceImage(); |
226 | uint8_t* dst_address = (uint8_t*)filterMgr->getDestinationAddress(); |
227 | const Palette* pal = filterMgr->getIndexedData()->getPalette(); |
228 | const RgbMap* rgbmap = filterMgr->getIndexedData()->getRgbMap(); |
229 | Target target = filterMgr->getTarget(); |
230 | int color, r, g, b, a; |
231 | GetPixelsDelegateIndexed delegate(pal, m_channel, target); |
232 | int x = filterMgr->x(); |
233 | int x2 = x+filterMgr->getWidth(); |
234 | int y = filterMgr->y(); |
235 | |
236 | for (; x<x2; ++x) { |
237 | // Avoid the non-selected region |
238 | if (filterMgr->skipPixel()) { |
239 | ++dst_address; |
240 | continue; |
241 | } |
242 | |
243 | delegate.reset(); |
244 | get_neighboring_pixels<IndexedTraits>(src, x, y, m_width, m_height, m_width/2, m_height/2, |
245 | m_tiledMode, delegate); |
246 | |
247 | if (target & TARGET_INDEX_CHANNEL) { |
248 | std::sort(m_channel[0].begin(), m_channel[0].end()); |
249 | *(dst_address++) = m_channel[0][m_ncolors/2]; |
250 | } |
251 | else { |
252 | color = get_pixel_fast<IndexedTraits>(src, x, y); |
253 | color = pal->getEntry(color); |
254 | |
255 | if (target & TARGET_RED_CHANNEL) { |
256 | std::sort(m_channel[0].begin(), m_channel[0].end()); |
257 | r = m_channel[0][m_ncolors/2]; |
258 | } |
259 | else |
260 | r = rgba_getr(color); |
261 | |
262 | if (target & TARGET_GREEN_CHANNEL) { |
263 | std::sort(m_channel[1].begin(), m_channel[1].end()); |
264 | g = m_channel[1][m_ncolors/2]; |
265 | } |
266 | else |
267 | g = rgba_getg(pal->getEntry(color)); |
268 | |
269 | if (target & TARGET_BLUE_CHANNEL) { |
270 | std::sort(m_channel[2].begin(), m_channel[2].end()); |
271 | b = m_channel[2][m_ncolors/2]; |
272 | } |
273 | else |
274 | b = rgba_getb(color); |
275 | |
276 | if (target & TARGET_ALPHA_CHANNEL) { |
277 | std::sort(m_channel[3].begin(), m_channel[3].end()); |
278 | a = m_channel[3][m_ncolors/2]; |
279 | } |
280 | else |
281 | a = rgba_geta(color); |
282 | |
283 | *(dst_address++) = rgbmap->mapColor(r, g, b, a); |
284 | } |
285 | } |
286 | } |
287 | |
288 | } // namespace filters |
289 | |