1 | // Aseprite |
2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
3 | // Copyright (C) 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/brightness_contrast_filter.h" |
13 | |
14 | #include "doc/image.h" |
15 | #include "doc/palette.h" |
16 | #include "doc/rgbmap.h" |
17 | #include "filters/filter_indexed_data.h" |
18 | #include "filters/filter_manager.h" |
19 | #include "gfx/hsl.h" |
20 | #include "gfx/rgb.h" |
21 | |
22 | #include <cmath> |
23 | |
24 | namespace filters { |
25 | |
26 | using namespace doc; |
27 | |
28 | const char* BrightnessContrastFilter::getName() |
29 | { |
30 | return "Brightness Contrast" ; |
31 | } |
32 | |
33 | BrightnessContrastFilter::BrightnessContrastFilter() |
34 | : m_brightness(0.0) |
35 | , m_contrast(0.0) |
36 | , m_cmap(256) |
37 | { |
38 | updateMap(); |
39 | } |
40 | |
41 | void BrightnessContrastFilter::setBrightness(double brightness) |
42 | { |
43 | m_brightness = brightness; |
44 | updateMap(); |
45 | } |
46 | |
47 | void BrightnessContrastFilter::setContrast(double contrast) |
48 | { |
49 | m_contrast = contrast; |
50 | updateMap(); |
51 | } |
52 | |
53 | void BrightnessContrastFilter::applyToRgba(FilterManager* filterMgr) |
54 | { |
55 | FilterIndexedData* fid = filterMgr->getIndexedData(); |
56 | const Palette* pal = fid->getPalette(); |
57 | Palette* newPal = (m_usePaletteOnRGB ? fid->getNewPalette(): nullptr); |
58 | const uint32_t* src_address = (uint32_t*)filterMgr->getSourceAddress(); |
59 | uint32_t* dst_address = (uint32_t*)filterMgr->getDestinationAddress(); |
60 | const int w = filterMgr->getWidth(); |
61 | const Target target = filterMgr->getTarget(); |
62 | |
63 | for (int x=0; x<w; x++) { |
64 | if (filterMgr->skipPixel()) { |
65 | ++src_address; |
66 | ++dst_address; |
67 | continue; |
68 | } |
69 | |
70 | color_t c = *(src_address++); |
71 | |
72 | if (newPal) { |
73 | int i = |
74 | pal->findExactMatch(rgba_getr(c), |
75 | rgba_getg(c), |
76 | rgba_getb(c), |
77 | rgba_geta(c), -1); |
78 | if (i >= 0) |
79 | c = newPal->getEntry(i); |
80 | } |
81 | else { |
82 | applyFilterToRgb(target, c); |
83 | } |
84 | |
85 | *(dst_address++) = c; |
86 | } |
87 | } |
88 | |
89 | void BrightnessContrastFilter::applyToGrayscale(FilterManager* filterMgr) |
90 | { |
91 | const uint16_t* src_address = (uint16_t*)filterMgr->getSourceAddress(); |
92 | uint16_t* dst_address = (uint16_t*)filterMgr->getDestinationAddress(); |
93 | const int w = filterMgr->getWidth(); |
94 | const Target target = filterMgr->getTarget(); |
95 | |
96 | for (int x=0; x<w; x++) { |
97 | if (filterMgr->skipPixel()) { |
98 | ++src_address; |
99 | ++dst_address; |
100 | continue; |
101 | } |
102 | |
103 | color_t c = *(src_address++); |
104 | int k = graya_getv(c); |
105 | int a = graya_geta(c); |
106 | |
107 | if (target & TARGET_GRAY_CHANNEL) k = m_cmap[k]; |
108 | |
109 | *(dst_address++) = graya(k, a); |
110 | } |
111 | } |
112 | |
113 | void BrightnessContrastFilter::applyToIndexed(FilterManager* filterMgr) |
114 | { |
115 | FilterIndexedData* fid = filterMgr->getIndexedData(); |
116 | |
117 | // Apply filter to pixels if there is selection (in other case, the |
118 | // change is global, so we have already applied the filter to the |
119 | // palette). |
120 | if (!filterMgr->isMaskActive()) |
121 | return; |
122 | |
123 | // Apply filter to color region |
124 | const Target target = filterMgr->getTarget(); |
125 | const Palette* pal = fid->getPalette(); |
126 | const RgbMap* rgbmap = fid->getRgbMap(); |
127 | const uint8_t* src_address = (uint8_t*)filterMgr->getSourceAddress(); |
128 | uint8_t* dst_address = (uint8_t*)filterMgr->getDestinationAddress(); |
129 | const int w = filterMgr->getWidth(); |
130 | |
131 | for (int x=0; x<w; x++) { |
132 | if (filterMgr->skipPixel()) { |
133 | ++src_address; |
134 | ++dst_address; |
135 | continue; |
136 | } |
137 | |
138 | color_t c = pal->getEntry(*(src_address++)); |
139 | applyFilterToRgb(target, c); |
140 | *(dst_address++) = rgbmap->mapColor(c); |
141 | } |
142 | } |
143 | |
144 | void BrightnessContrastFilter::onApplyToPalette(FilterManager* filterMgr, |
145 | const PalettePicks& picks) |
146 | { |
147 | const Target target = filterMgr->getTarget(); |
148 | FilterIndexedData* fid = filterMgr->getIndexedData(); |
149 | const Palette* pal = fid->getPalette(); |
150 | Palette* newPal = fid->getNewPalette(); |
151 | |
152 | int i = 0; |
153 | for (bool state : picks) { |
154 | if (!state) { |
155 | ++i; |
156 | continue; |
157 | } |
158 | |
159 | color_t c = pal->getEntry(i); |
160 | applyFilterToRgb(target, c); |
161 | newPal->setEntry(i, c); |
162 | ++i; |
163 | } |
164 | } |
165 | |
166 | void BrightnessContrastFilter::applyFilterToRgb( |
167 | const Target target, doc::color_t& c) |
168 | { |
169 | int r = rgba_getr(c); |
170 | int g = rgba_getg(c); |
171 | int b = rgba_getb(c); |
172 | int a = rgba_geta(c); |
173 | |
174 | if (target & TARGET_RED_CHANNEL ) r = m_cmap[r]; |
175 | if (target & TARGET_GREEN_CHANNEL) g = m_cmap[g]; |
176 | if (target & TARGET_BLUE_CHANNEL ) b = m_cmap[b]; |
177 | |
178 | c = rgba(r, g, b, a); |
179 | } |
180 | |
181 | void BrightnessContrastFilter::updateMap() |
182 | { |
183 | int max = int(m_cmap.size()); |
184 | for (int u=0; u<max; ++u) { |
185 | double x = double(u) / double(max-1); |
186 | double y = (m_contrast+1.0) * (x - 0.5) + 0.5; |
187 | y = y*(1.0+m_brightness); |
188 | y = std::clamp(y, 0.0, 1.0); |
189 | m_cmap[u] = int(255.5 * y); |
190 | } |
191 | } |
192 | |
193 | } // namespace filters |
194 | |