Euphoria
collage.cc
Go to the documentation of this file.
1 #include "core/collage.h"
2 
3 #include <range/v3/all.hpp>
4 
5 #include "core/image.h"
6 #include "base/rgb.h"
7 #include "core/image_draw.h"
8 #include "core/pack.h"
9 #include "base/cint.h"
10 #include "base/numeric.h"
11 
12 
13 namespace eu::core
14 {
15  std::vector<size2i> collect_sizes
16  (
17  const std::vector<Image>& images,
18  int padding
19  )
20  {
21  auto sizes = std::vector<size2i>{};
22 
23  for(const auto& img: images)
24  {
25  sizes.emplace_back(size2i::create_from_width_height
26  (
27  img.width + padding,
28  img.height + padding
29  ));
30  }
31 
32  return sizes;
33  }
34 
35 
36  Image
38  (
39  const std::vector<vec2i>& positions,
40  const std::vector<Image>& images,
41  const size2i& size,
42  const Rgbi& background_color
43  )
44  {
45  auto composed_image = Image{};
46  composed_image.setup_with_alpha_support
47  (
48  size.width,
49  size.height
50  );
51  clear(&composed_image, background_color);
52 
53  for(const auto& [position, image]: ranges::views::zip(positions, images))
54  {
56  (
57  &composed_image,
58  position,
59  image,
62  );
63  }
64 
65  return composed_image;
66  }
67 
68 
69  size2i
71  (
72  const size2i& default_size,
73  std::vector<vec2i>* positions,
74  const std::vector<Image>& images,
75  int padding
76  )
77  {
78  std::optional<Recti> bb = std::nullopt;
79 
80  for(const auto& [position, img]: ranges::views::zip(*positions, images))
81  {
82  const auto image_width = img.width;
83  const auto image_height = img.height;
84  const auto& rect = Recti::from_bottom_left_width_height
85  (
86  vec2i(position.x, position.y),
87  image_width + padding,
88  image_height + padding
89  );
90  if(bb.has_value())
91  { bb->include(rect); }
92  else
93  { bb = rect; }
94  }
95  if(!bb) { return default_size; }
96 
97  const auto size = bb->get_size();
98  const auto dx = -bb->left;
99  const auto dy = -bb->bottom;
100 
101  for(auto& position: *positions)
102  {
103  position += vec2i(dx, dy);
104  }
105 
106  return size2i::create_from_width_height(size.width + padding, size.height + padding);
107  }
108 
110 
111 
112  std::vector<vec2i>
114  (
115  const size2i& image_size,
116  const std::vector<Image>& images,
117  const std::vector<std::string>& files,
118  int padding
119  )
120  {
121  const auto image_sizes = collect_sizes(images, padding);
122  const auto packed = pack(image_size, image_sizes);
123 
124  auto ret = std::vector<vec2i>{};
125 
126  int i = 0;
127  for(const auto& rect: packed)
128  {
129  const auto& file = files[i];
130  i += 1;
131 
132  if(rect.has_value() == false)
133  {
134  std::cerr << "failed to pack " << file << "\n";
135  }
136  else
137  {
138  ret.emplace_back(rect->get_bottom_left());
139  }
140  }
141 
142  return ret;
143  }
144 
145 
146  std::optional<Image>
148  (
149  const std::vector<Image>& images,
150  const std::vector<std::string>& files,
151  const size2i& requested_size,
152  int padding,
153  Rgbi background_color,
154  bool should_pack_image
155  )
156  {
157  if( requested_size.width < padding ||
158  requested_size.height < padding)
159  {
160  return std::nullopt;
161  }
162 
163  const auto image_size = size2i::create_from_width_height
164  (
165  requested_size.width - padding,
166  requested_size.height - padding
167  );
168 
169  // pack images
170  auto packed = pack_image(image_size, images, files, padding);
171 
172  if(packed.empty())
173  {
174  return std::nullopt;
175  }
176 
177  // optionally reduce image size
178  const auto size = should_pack_image
179  ? pack_tight(requested_size, &packed, images, padding)
180  : requested_size
181  ;
182 
183  // border-theory:
184  // reuqested_size is decreased with padding
185  // the tighlty packed size is increased with padding
186  // the packed image-sizes is increased with padding
187  // and finally all the images are offseted by padding
188  // all to keep padding-sized border between the images
189  for(auto& position: packed)
190  {
191  position += vec2i(padding, padding);
192  }
193 
194  // draw new image
195  auto composed_image = draw_image(packed, images, size, background_color);
196 
197  return composed_image;
198  }
199 
200 
202 
203 
204  std::pair<std::vector<vec2i>, size2i>
206  (
207  const std::vector<Image>& images,
208  int padding,
209  bool top_to_bottom
210  )
211  {
212  const auto images_per_row = c_float_to_int
213  (
214  ceil
215  (
216  sqrt
217  (
219  (
220  images.size()
221  )
222  )
223  )
224  );
225 
226  auto ret = std::vector<vec2i>{};
227 
228  int x = padding;
229  int y = padding;
230  int current_height = 0;
231  int column = 0;
232  int max_x = 0;
233 
234  auto add_row = [&]
235  {
236  max_x = max(max_x, x);
237  column = 0;
238  x = padding;
239  y += current_height + padding;
240  current_height = 0;
241  };
242 
243  for(const auto& src: images)
244  {
245  const auto width = src.width;
246  const auto height = src.height;
247 
248  ret.emplace_back
249  (
250  x, y
251  );
252 
253  x += width + padding;
254  current_height = max(current_height, height);
255  column += 1;
256 
257  if(column >= images_per_row)
258  {
259  add_row();
260  }
261  }
262 
263  if(column != 0)
264  {
265  add_row();
266  }
267 
268  const auto image_width = max_x;
269  const auto image_height = y;
270 
271  // we lay out left->right, bottom->top since that is easer
272  // but top->bottom is ieasier to read, so we provide a option
273  // to switch to that, and we do that by inverting it at the end
274  if(top_to_bottom)
275  {
276  const auto into_pointer = [](vec2i& v) -> vec2i* { return &v; };
277  using namespace ranges::views;
278  for
279  (
280  const auto& [position, img]:
281  zip(ret|transform(into_pointer), images)
282  )
283  {
284  position->y = image_height - (position->y + img.height);
285  }
286  }
287 
288  return {ret, size2i::create_from_width_height(image_width, image_height)};
289  }
290 
291 
292  Image
294  (
295  const std::vector<Image>& images,
296  int padding,
297  Rgbi background_color,
298  bool top_to_bottom
299  )
300  {
301  // layout images in grid and calculate image size from grid
302  const auto [image_grid, size] = lay_out_in_a_grid(images, padding, top_to_bottom);
303 
304  // draw new image
305  auto composed_image = draw_image(image_grid, images, size, background_color);
306 
307  return composed_image;
308  }
309 
310 }
311 
void clear(Image *image, const Rgbai &color)
Definition: image_draw.cc:32
Image draw_image(const std::vector< vec2i > &positions, const std::vector< Image > &images, const size2i &size, const Rgbi &background_color)
Definition: collage.cc:38
std::pair< std::vector< vec2i >, size2i > lay_out_in_a_grid(const std::vector< Image > &images, int padding, bool top_to_bottom)
Definition: collage.cc:206
std::vector< std::optional< Recti > > pack(const size2i &container, const std::vector< size2i > &to_pack)
Definition: pack.cc:16
std::vector< vec2i > pack_image(const size2i &image_size, const std::vector< Image > &images, const std::vector< std::string > &files, int padding)
Definition: collage.cc:114
std::vector< size2i > collect_sizes(const std::vector< Image > &images, int padding)
Definition: collage.cc:16
size2i pack_tight(const size2i &default_size, std::vector< vec2i > *positions, const std::vector< Image > &images, int padding)
Definition: collage.cc:71
void paste_image(Image *dest_image, const vec2i &position, const Image &source_image, BlendMode blend_mode, PixelsOutside clip)
Definition: image_draw.cc:553
float sqrt(float r)
Definition: numeric.cc:101
constexpr float c_sizet_to_float(std::size_t f)
Definition: cint.h:43
constexpr int c_float_to_int(float f)
Definition: cint.h:36
std::vector< std::pair< A, B > > zip(const std::vector< A > &as, const std::vector< B > &bs)
Definition: functional.h:19
float ceil(float v)
Definition: numeric.cc:42
size2f max(const size2f lhs, const size2f rhs)
Definition: size2.cc:149
static Recti from_bottom_left_width_height(const vec2i &bl, int width, int height)
Definition: rect.cc:548
Definition: rgb.h:26
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
static size2i create_from_width_height(int w, int h)
Definition: size2.cc:211
int width
Definition: size2.h:36
int height
Definition: size2.h:37
Definition: vec2.h:72