Euphoria
imagefilters.cc
Go to the documentation of this file.
1 #include "core/imagefilters.h"
2 
3 #include "core/image.h"
4 #include "core/palette.h"
5 #include "core/table.h"
6 #include "base/vec3.h"
7 
8 
9 namespace eu::core
10 {
11  namespace
12  {
13  Rgbai make_gray(U8 g, U8 a)
14  {
15  return {Rgbi{g, g, g}, a};
16  }
17  }
18 
19 
20  //
21  // https://twitter.com/FreyaHolmer/status/1116502994684530688
22  Rgbai
24  {
25  switch(grayscale)
26  {
27  case Grayscale::r: return make_gray(c.r, c.a);
28  case Grayscale::g: return make_gray(c.g, c.a);
29  case Grayscale::b: return make_gray(c.b, c.a);
30  case Grayscale::a: return make_gray(c.a, c.a);
31  case Grayscale::max: return make_gray(std::max(c.r, std::max(c.g, c.b)), c.a);
32  case Grayscale::gamma:
33  {
34  const auto d = dot(to_rgb(c), Rgb(0.22f, 0.707f, 0.071f));
35  return {to_rgbi(Rgb(d)), c.a};
36  }
37  case Grayscale::linear:
38  {
39  const auto d = dot(to_rgb(c), Rgb(0.0397f, 0.4580f, 0.0061f));
40  return {to_rgbi(Rgb(d)), c.a};
41  }
42  case Grayscale::average:
43  {
44  const auto cc = to_rgb(c);
45  const auto g = (cc.r + cc.g + cc.b) / 3;
46  return {to_rgbi(Rgb(g)), c.a};
47  }
48 
49  default:
50  return make_gray(c.a, c.a);
51  }
52  }
53 
54 
55  void
56  make_grayscale(Image* image, Grayscale grayscale)
57  {
58  image->run_image_filter([grayscale](const Rgbai& c)
59  {
60  return make_grayscale(c, grayscale);
61  });
62  }
63 
64  void
65  match_palette(Image* image, const Palette& palette)
66  {
67  image->run_image_filter([&palette](const Rgbai& c) {
68  const auto cc = to_rgbi(c);
69  const auto nc = palette.get_closest_color(cc);
70 
71  return Rgbai(nc, c.a);
72  });
73  }
74 
75  template <typename C>
76  [[nodiscard]] Image
77  create_new_image_from(const Image& image_source, C callback)
78  {
79  Image ret;
80  if(image_source.has_alpha)
81  {
82  ret.setup_with_alpha_support(image_source.width, image_source.height, -1);
83  }
84  else
85  {
86  ret.setup_no_alpha_support(image_source.width, image_source.height, -1);
87  }
88  ret.set_all_top_bottom(callback);
89  return ret;
90  }
91 
92  void
93  match_palette_dither(Image* image, const Palette& palette)
94  {
95  struct ColorError
96  {
97  float r = 0;
98  float g = 0;
99  float b = 0;
100  };
101  auto errors = Table<ColorError>::from_width_height(image->width, image->height);
102  const auto errors_range = errors.get_indices();
103 
104  *image = create_new_image_from(*image, [&](int x, int y)
105  {
106  auto pixel = image->get_pixel(x, y);
107  auto new_color = to_rgb(pixel);
108  const auto pixel_error = errors[{x, y}];
109  new_color.r += pixel_error.r;
110  new_color.g += pixel_error.g;
111  new_color.b += pixel_error.b;
112  new_color = clamp(new_color);
113  const auto palette_color = palette.get_closest_color(to_rgbi(new_color));
114 
115  const auto pcf = to_rgb(palette_color);
116  const auto error = ColorError
117  {
118  new_color.r - pcf.r,
119  new_color.g - pcf.g,
120  new_color.b - pcf.b
121  };
122  const auto floyd_steinberg = std::vector<std::pair<vec2i, float>>
123  {
124  {vec2i(1, 0), 7.0f / 16.0f},
125  {vec2i(1, -1), 1.0f / 16.0f},
126  {vec2i(0, -1), 5.0f / 16.0f},
127  {vec2i(-1, -1), 3.0f / 16.0f}
128  };
129 
130  for(auto fs: floyd_steinberg)
131  {
132  auto nx = x + fs.first.x;
133  auto ny = y + fs.first.y;
134  auto factor = fs.second;
135  if(errors_range.contains_inclusive(nx, ny))
136  {
137  auto& e = errors[{nx, ny}];
138  e.r += factor * error.r;
139  e.g += factor * error.g;
140  e.b += factor * error.b;
141  }
142  }
143 
144  return Rgbai(palette_color, pixel.a);
145  });
146  }
147 
148  vec3f
149  c_vec3(const Rgb f)
150  {
151  return {f.r, f.g, f.b};
152  }
153 
154  vec3f
155  c_vec3(const Rgbi c)
156  {
157  return c_vec3(to_rgb(c));
158  }
159 
160  vec3f
161  c_vec3(const Rgbai c)
162  {
163  return c_vec3(to_rgb(c));
164  }
165 
166  void
167  filter_edge_detection(Image* image, float r)
168  {
169  *image = create_new_image_from(*image, [&](int x, int y) {
170  const auto pixel = c_vec3(image->get_pixel(x, y));
171  const auto top
172  = y == image->height - 1
173  ? false
174  : (pixel - c_vec3(image->get_pixel(x, y + 1)))
175  .get_length()
176  >= r;
177  const auto left
178  = x == 0 ? false
179  : (pixel - c_vec3(image->get_pixel(x - 1, y)))
180  .get_length()
181  >= r;
182  const bool edge = top || left;
183  const auto c = edge ? NamedColor::white : NamedColor::black;
184  return Rgbai(c, 255);
185  });
186  }
187 
188 
189  void
190  filter_color_detection(Image* image, Rgb color, float r)
191  {
192  const auto basis = c_vec3(color);
193  image->run_image_filter([&](const Rgbai pixel) {
194  const auto check = (c_vec3(pixel) - basis).get_length() <= r;
195  const auto c = check ? NamedColor::white : NamedColor::black;
196  return Rgbai(c, 255);
197  });
198  }
199 
200  template <typename C>
201  void
203  {
204  std::vector<U8> lut;
205  lut.reserve(256);
206  for(int lut_index = 0; lut_index < 256; lut_index++)
207  {
208  lut.emplace_back(c(lut_index));
209  }
210  image->run_image_filter([&](const Rgbai pixel)
211  {
212  return Rgbai
213  (
214  Rgbi
215  (
216  lut[pixel.r],
217  lut[pixel.g],
218  lut[pixel.b]
219  ),
220  pixel.a
221  );
222  });
223  }
224 
225  namespace
226  {
227  U8 keep_within_u8(int i)
228  {
229  return static_cast<U8>(keep_within(make_range(0, 255), i));
230  }
231  }
232 
233  void
234  change_brightness(Image* image, int change)
235  {
236  run_lut_transform(image, [&](int i) {
237  return keep_within_u8(i + change);
238  });
239  }
240 
241  void
242  change_contrast(Image* image, const Angle& contrast)
243  {
244  const auto tc = tan(contrast);
245  run_lut_transform(image, [&](int i) -> U8 {
246  const auto f = c_int_to_float(i);
247  const auto a = 128.0f + 128.0f * tc;
248  const auto b = 128.0f - 128.0f * tc;
249  if(f < a && b < f)
250  {
251  return keep_within_u8(c_float_to_int((f - 128.0f) / tc) + 128);
252  }
253  else if(f > a)
254  {
255  return 255;
256  }
257  else
258  {
259  return 0;
260  }
261  });
262  }
263 
264 }
constexpr ParseResult error
no error occurred
Definition: argparse.h:73
void filter_edge_detection(Image *image, float r)
vec3f c_vec3(const Rgb f)
void change_contrast(Image *image, const Angle &contrast)
void match_palette(Image *image, const Palette &palette)
Definition: imagefilters.cc:65
void run_lut_transform(Image *image, C c)
void filter_color_detection(Image *image, Rgb color, float r)
void match_palette_dither(Image *image, const Palette &palette)
Definition: imagefilters.cc:93
Image create_new_image_from(const Image &image_source, C callback)
Definition: imagefilters.cc:77
void change_brightness(Image *image, int change)
Rgbai make_grayscale(Rgbai c, Grayscale grayscale)
Definition: imagefilters.cc:23
T keep_within(const Range< T > &range, T value)
Definition: range.h:115
Rgbi to_rgbi(const Rgb &c)
Definition: rgb.cc:352
constexpr int c_float_to_int(float f)
Definition: cint.h:36
Range< T > make_range(T min, T max)
Definition: range.h:39
Rgb to_rgb(const Rgbi &c)
Definition: rgb.cc:229
float tan(const Angle &ang)
Definition: angle.cc:75
std::uint8_t U8
Definition: ints.h:15
constexpr float c_int_to_float(int i)
Definition: cint.h:50
size2f max(const size2f lhs, const size2f rhs)
Definition: size2.cc:149
float dot(const quatf &lhs, const quatf &rhs)
Definition: quat.cc:363
Rgb clamp(const Rgb &c)
Definition: rgb.cc:144
Definition: rgb.h:62
Definition: rgb.h:45
U8 g
Definition: rgb.h:47
U8 r
Definition: rgb.h:46
U8 b
Definition: rgb.h:48
U8 a
Definition: rgb.h:49
Definition: rgb.h:26
bool has_alpha
Definition: image.h:34
void setup_with_alpha_support(int image_width, int image_height, int default_value=0)
if default value is negative, default value is ignored, otherwise its the default value for both R,...
Definition: image.cc:40
void setup_no_alpha_support(int image_width, int image_height, int default_value=0)
Definition: image.cc:52
void set_all_top_bottom(TFunc f)
Definition: image.h:95
int height
Definition: image.h:33
const Rgbi & get_closest_color(const Rgbi &c) const
Definition: palette.cc:39
static Table from_width_height(Idx width, Idx height, T d=T())
Definition: table.h:23
Definition: vec2.h:72
Definition: vec3.h:48