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
24namespace filters {
25
26using namespace doc;
27
28const char* BrightnessContrastFilter::getName()
29{
30 return "Brightness Contrast";
31}
32
33BrightnessContrastFilter::BrightnessContrastFilter()
34 : m_brightness(0.0)
35 , m_contrast(0.0)
36 , m_cmap(256)
37{
38 updateMap();
39}
40
41void BrightnessContrastFilter::setBrightness(double brightness)
42{
43 m_brightness = brightness;
44 updateMap();
45}
46
47void BrightnessContrastFilter::setContrast(double contrast)
48{
49 m_contrast = contrast;
50 updateMap();
51}
52
53void 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
89void 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
113void 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
144void 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
166void 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
181void 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