Euphoria
palette_extract.cc
Go to the documentation of this file.
1 #include "core/palette_extract.h"
2 
3 #include <functional>
4 #include <algorithm>
5 
6 #include "base/minmax.h"
7 #include "core/subvec.h"
8 #include "core/image.h"
9 #include "base/cint.h"
10 
11 
12 namespace
13 {
14  using namespace eu;
15  using namespace eu::core;
16 
17 
18  std::vector<Rgbi>
19  extract_all_colors(const Image& image)
20  {
21  auto ret = std::vector<Rgbi>{};
22  ret.reserve(ret.size() + eu::c_int_to_sizet(image.height) * eu::c_int_to_sizet(image.width));
23 
24  for(int y=0; y<image.height; y+=1)
25  {
26  for(int x=0; x<image.width; x+=1)
27  {
28  const auto color = to_rgbi(image.get_pixel(x, y));
29  ret.emplace_back(color);
30  }
31  }
32 
33  return ret;
34  }
35 
36 
37  enum class SortRange
38  {
39  r, g, b
40  };
41 
42 
43  float
44  get_value(SortRange range, const Rgbi& c)
45  {
46  switch(range)
47  {
48  case SortRange::r: return to_rgb(c).r;
49  case SortRange::g: return to_rgb(c).g;
50  case SortRange::b: return to_rgb(c).b;
51  default: return 0;
52  }
53  }
54 
55 
56  std::tuple<SortRange, Range<float>>
57  find_greatest_sort_range(SubVec<Rgbi> colors)
58  {
59  using Tu = std::tuple<SortRange, Range<float>>;
60 
61  const auto [min_values, max_values] = find_min_max_ranges<3, float>
62  (
63  colors,
64  [](const Rgbi& c) -> std::array<float, 3>
65  {
66  return
67  {
68  get_value(SortRange::r, c),
69  get_value(SortRange::g, c),
70  get_value(SortRange::b, c)
71  };
72  }
73  );
74  // init-capture due to a bug in clang/gcc/standard
75  // https://www.reddit.com/r/cpp/comments/68vhir/whats_the_rationale_for_this_reference_to_local/
76  auto make = [&, miv = std::move(min_values), mav = std::move(max_values)] (SortRange r, size_t i) -> Tu
77  {
78  return std::make_tuple(r, Range{miv[i], mav[i]});
79  };
80  auto ranges = std::vector<Tu>
81  {
82  make(SortRange::r, 0),
83  make(SortRange::g, 1),
84  make(SortRange::b, 2)
85  };
86  std::sort
87  (
88  ranges.begin(), ranges.end(), [](const Tu& lhs, const Tu& rhs)
89  {
90  return std::get<1>(lhs).get_distance() > std::get<1>(rhs).get_distance();
91  }
92  );
93  return ranges[0];
94  }
95 
96 
97  void
98  sort(SortRange sort_range, SubVec<Rgbi> colors)
99  {
100  auto sort = [&](std::function<float (const Rgbi& c)> conv)
101  {
102  std::sort(colors.begin(), colors.end(), [&]
103  (
104  const Rgbi& lhs, const Rgbi& rhs)
105  {
106  return conv(lhs) < conv(rhs);
107  }
108  );
109  };
110 
111  switch(sort_range)
112  {
113  case SortRange::r: sort([](const Rgbi& c) -> float { return c.r; }); break;
114  case SortRange::g: sort([](const Rgbi& c) -> float { return c.g; }); break;
115  case SortRange::b: sort([](const Rgbi& c) -> float { return c.b; }); break;
116  }
117  }
118 
119 
120 
121  size_t
122  find_median_index(SortRange sort, SubVec<Rgbi> colors, Range<float> range)
123  {
124  const auto median = range.get_distance()/2 + range.lower_bound;
125  // todo(Gustav): make non-linear
126  for(size_t index = 0; index<colors.size()-1; index+=1)
127  {
128  if(get_value(sort, colors[eu::c_sizet_to_int(index + 1)]) >= median)
129  {
130  return index;
131  }
132  }
133  DIE("shouldn't happen...");
134  return 0;
135  }
136 
137 
138 
139 
140  std::vector<Rgbi>
141  extract_palette_median_cut(SubVec<Rgbi> src, int depth, bool split_middle)
142  {
143  if(src.empty())
144  {
145  return {};
146  }
147 
148  if(depth <= 0 || src.size() < 2)
149  {
150  auto sum = Rgb{0,0,0};
151  float total = 0;
152  for(const auto& c: src)
153  {
154  sum += to_rgb(c);
155  total += 1;
156  }
157 
158  const auto r = to_rgbi(sum / total);
159 
160  return {r};
161  }
162 
163  const auto [range, rrange] = find_greatest_sort_range(src);
164 
165  sort(range, src);
166 
167  const auto median = split_middle
168  ? src.size() / 2
169  : find_median_index(range, src, rrange)
170  ;
171  const auto left = src.sub(0, median);
172  const auto right = src.sub(median, src.size());
173 
174  auto ret = extract_palette_median_cut(left, depth - 1, split_middle);
175  const auto rhs = extract_palette_median_cut(right, depth - 1, split_middle);
176  ret.insert(ret.end(), rhs.begin(), rhs.end());
177 
178  return ret;
179  }
180 }
181 
182 namespace eu::core
183 {
184  std::vector<Rgbi>
185  extract_palette_median_cut(const Image& image, int depth, bool middle_split)
186  {
187  auto all_colors = extract_all_colors(image);
188  auto colors = ::extract_palette_median_cut(SubVec{&all_colors}, depth, middle_split);
189  return colors;
190  }
191 }
192 
#define DIE(message)
Definition: assert.h:67
constexpr unit3f right
Definition: vec3.h:133
constexpr unit3f left
Definition: vec3.h:134
std::vector< Rgbi > extract_palette_median_cut(const Image &image, int depth, bool middle_split)
constexpr ShaderAttribute color
Definition: assert.h:90
int c_sizet_to_int(size_t t)
Definition: cint.cc:11
Rgbi to_rgbi(const Rgb &c)
Definition: rgb.cc:352
size_t c_int_to_sizet(int i)
Definition: cint.cc:35
Rgb to_rgb(const Rgbi &c)
Definition: rgb.cc:229
T get_distance() const
Definition: range.h:31
T lower_bound
Definition: range.h:17
Definition: rgb.h:62
float b
Definition: rgb.h:65
float g
Definition: rgb.h:64
float r
Definition: rgb.h:63
Definition: rgb.h:26
bool empty() const
Definition: subvec.h:56
std::size_t size() const
Definition: subvec.h:50
Iterator begin() const
Definition: subvec.h:38
SubVec< T > sub(std::size_t start, std::size_t end)
Definition: subvec.h:32
Iterator end() const
Definition: subvec.h:44