Euphoria
image_draw.cc
Go to the documentation of this file.
1 #include "core/image_draw.h"
2 
3 #include "base/numeric.h"
4 #include "assert/assert.h"
5 #include "base/range.h"
6 #include "core/loadedfont.h"
7 #include "core/utf8.h"
8 #include "core/rgb_blend.h"
9 #include "base/minmax.h"
10 #include "core/intersection.h"
11 
12 #include "log/log.h"
13 
14 #include <utility>
15 #include <cmath>
16 
17 
18 namespace eu::core
19 {
20  Recti
21  on_whole_image(const Image& image)
22  {
24  (
25  vec2i{0, image.height},
26  image.width,
27  image.height
28  );
29  }
30 
31  void
32  clear(Image* image, const Rgbai& color)
33  {
34  ASSERT(image);
35  return draw_rect(image, color, on_whole_image(*image));
36  }
37 
38  void
39  draw_rect(Image* image, const Rgbai& color, const Recti& rect)
40  {
41  ASSERT(image);
42  const int left = rect.get_top_left().x;
43  const int right = rect.get_top_right().x;
44  const int top = rect.get_top_left().y;
45  const int bottom = rect.get_bottom_left().y;
46  // ASSERTX(left >= 0, left);
47  // ASSERTX(bottom >= 0, bottom);
48  for(int y = bottom; y < top; ++y)
49  {
50  if(y < 0 || y >= image->height)
51  {
52  continue;
53  }
54  for(int x = left; x < right; ++x)
55  {
56  if(x < 0 || x >= image->width)
57  {
58  continue;
59  }
60  image->set_pixel(x, y, color);
61  }
62  }
63  }
64 
65 
66  void
67  draw_square(Image* image, const Rgbai& color, int x, int y, int size)
68  {
69  ASSERT(image);
70  draw_rect
71  (
72  image,
73  color,
74  // is the +1 right?
75  Recti::from_top_left_width_height(vec2i{x, y + 1}, size, size)
76  );
77  }
78 
79 
80  namespace
81  {
82  Rectf
83  calculate_bounding_rect(const std::vector<vec2f>& poly)
84  {
85  const auto [min, max] = find_min_max<vec2f>
86  (
87  poly,
88  [](const auto& lhs, const auto& rhs)
89  {
90  return vec2f
91  {
92  std::min(lhs.x, rhs.x),
93  std::min(lhs.y, rhs.y)
94  };
95  },
96  [](const auto& lhs, const auto& rhs)
97  {
98  return vec2f
99  {
100  std::max(lhs.x, rhs.x),
101  std::max(lhs.y, rhs.y)
102  };
103  }
104  );
105 
106  return Rectf::from_left_right_bottom_top(min.x, max.x, min.y, max.y);
107  }
108 
109  bool
110  does_ray_intersect_segment(const vec2f& u, const vec2f& a, const vec2f& b)
111  {
112  // todo(Gustav): move to math
113  return
114  (a.y > u.y) != (b.y > u.y) &&
115  u.x < (b.x - a.x) * (u.y - a.y) / (b.y - a.y) + a.x
116  ;
117  }
118 
119  bool
120  point_is_in_poly(const vec2f& p, const std::vector<vec2f>& poly)
121  {
122  // todo(Gustav): make pretty and move to custom struct
123  if(poly.size() < 3)
124  {
125  return false;
126  }
127 
128  auto previous_point = poly[0];
129  auto in = does_ray_intersect_segment(p, *poly.rbegin(), previous_point);
130 
131  for(auto poly_iterator = poly.begin() + 1; poly_iterator != poly.end(); ++poly_iterator)
132  {
133  const auto current_point = *poly_iterator;
134  if(does_ray_intersect_segment(p, previous_point, current_point))
135  {
136  in = !in;
137  }
138  previous_point = current_point;
139  }
140 
141  return in;
142  }
143  }
144 
145  void
146  fill_poly(Image* image, const Rgbai& color, const std::vector<vec2f>& poly)
147  {
148  ASSERT(image);
149 
150  const auto rect = calculate_bounding_rect(poly);
151  const int left = c_float_to_int(rect.get_top_left().x);
152  const int right = c_float_to_int(rect.get_top_right().x);
153  const int top = c_float_to_int(rect.get_top_left().y);
154  const int bottom = c_float_to_int(rect.get_bottom_left().y);
155 
156  // ASSERTX(left >= 0, left);
157  // ASSERTX(bottom >= 0, bottom);
158  for(int y = bottom; y < top; ++y)
159  {
160  if(y < 0 || y >= image->height) { continue; }
161  for(int x = left; x < right; ++x)
162  {
163  if(x < 0 || x >= image->width) { continue; }
164 
165  if(point_is_in_poly(vec2f{c_int_to_float(x), c_int_to_float(y)}, poly))
166  {
167  image->set_pixel(x, y, color);
168  }
169  }
170  }
171  }
172 
173  void
175  (
176  Image* image,
177  const Rgb& color,
178  const vec2i& center,
179  float radius,
180  float softness,
181  float inner
182  )
183  {
184  ASSERT(image);
185  const int left = eu::max(0, center.x - floor_to_int(radius - softness));
186  const int right = eu::min
187  (
188  image->width,
189  ceil_to_int(c_int_to_float(center.x) + radius + softness)
190  );
191  const int top = eu::max(0, floor_to_int(c_int_to_float(center.y) - radius - softness));
192  const int bottom = eu::min
193  (
194  image->height,
195  center.y + ceil_to_int(radius + softness)
196  );
197 
198  // color modes
199  // nothing INNER-SOFTNESS fade INNER full RADIUS fade RADIUS+SOFTNESS nothing
200 
201  for(int y = top; y < bottom; ++y)
202  {
203  for(int x = left; x < right; ++x)
204  {
205  // todo(Gustav): use length squared!
206  const float sq = vec2f::from_to
207  (
208  vec2f{static_cast<float>(x), static_cast<float>(y)},
209  center.to_f()
210  ).get_length();
211  bool blend = false;
212  float blend_factor = 1.0f;
213 
214  const auto a = make_range(inner - softness, inner);
215  const auto b = make_range(radius, radius + softness);
216 
217  if(is_within(a, sq))
218  {
219  blend_factor = to01(a, sq);
220  blend = true;
221  }
222  else if(is_within(b, sq))
223  {
224  blend_factor = 1.0f - to01(b, sq);
225  blend = true;
226  }
227  else if(is_within(make_range(inner, radius), sq))
228  {
229  // full color
230  }
231  else
232  {
233  // outside
234  continue;
235  }
236 
237  const Rgb paint_color = blend
238  ? lerp_rgb
239  (
240  to_rgb(image->get_pixel(x, y)),
241  blend_factor,
242  color
243  )
244  : color
245  ;
246 
247  image->set_pixel(x, y, Rgbai{paint_color});
248  }
249  }
250  }
251 
252  void
254  (
255  Image* image,
256  const Rgbai& color,
257  const vec2i& from,
258  const vec2i& to
259  )
260  {
261  ASSERT(image);
262 
263  // reference:
264  // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
265 
266  auto plot = [&](int x, int y)
267  {
268  image->set_pixel(x, y, color);
269  };
270 
271  auto plot_line_low = [&](int x0, int y0, int x1, int y1)
272  {
273  auto dx = x1 - x0;
274  auto dy = y1 - y0;
275  auto yi = 1;
276 
277  if(dy < 0)
278  {
279  yi = -1;
280  dy = -dy;
281  }
282 
283  auto difference = 2 * dy - dx;
284  auto y = y0;
285 
286  for(int x=x0; x<x1; x+=1)
287  {
288  plot(x,y);
289  if(difference > 0)
290  {
291  y = y + yi;
292  difference = difference - 2 * dx;
293  }
294  difference = difference + 2 * dy;
295  }
296  };
297 
298  auto plot_line_high = [&](int x0, int y0, int x1, int y1)
299  {
300  int dx = x1 - x0;
301  int dy = y1 - y0;
302  int xi = 1;
303  if(dx < 0)
304  {
305  xi = -1;
306  dx = -dx;
307  }
308  int difference = 2 * dx - dy;
309  int x = x0;
310 
311  for(int y=y0; y<y1; y+=1)
312  {
313  plot(x,y);
314  if(difference > 0)
315  {
316  x = x + xi;
317  difference = difference - 2 * dy;
318  }
319  difference = difference + 2 * dx;
320  }
321  };
322 
323  const auto x0 = from.x;
324  const auto y0 = from.y;
325  const auto x1 = to.x;
326  const auto y1 = to.y;
327  if(std::abs(y1 - y0) < std::abs(x1 - x0))
328  {
329  if(x0 > x1) { plot_line_low(x1, y1, x0, y0); }
330  else { plot_line_low(x0, y0, x1, y1); }
331  }
332  else
333  {
334  if(y0 > y1) { plot_line_high(x1, y1, x0, y0); }
335  else { plot_line_high(x0, y0, x1, y1); }
336  }
337  }
338 
339 
340  void
342  (
343  Image* image,
344  const Rgb& color,
345  const vec2i& from,
346  const vec2i& to
347  )
348  {
349  ASSERT(image);
350  return draw_line_antialiased
351  (
352  image,
353  color,
354  from.to_f(),
355  to.to_f()
356  );
357  }
358 
359 
360  void
362  (
363  Image* image,
364  const Rgb& color,
365  const vec2f& from,
366  const vec2f& to
367  )
368  {
369  // reference:
370  // https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm
371 
372  auto ipart = [&](float x) -> int { return floor_to_int(x); };
373  auto round = [&](float x) -> int { return ipart(x + 0.5f); };
374  auto fpart = [&](float x) -> float { return x - std::floor(x); };
375  auto rfpart = [&](float x) -> float { return 1.0f - fpart(x); };
376 
377  auto plot = [&](int x, int y, float c)
378  {
379  // plot the pixel at (x, y) with brightness c (where 0 ≤ c ≤ 1)
380  const bool valid_x = is_within_inclusive_as_int(0, x, image->width - 1);
381  const bool valid_y = is_within_inclusive_as_int(0, y, image->height - 1);
382  if(valid_x && valid_y)
383  {
384  const Rgb paint_color = lerp_rgb
385  (
386  to_rgb(image->get_pixel(x, y)),
387  c,
388  color
389  );
390  image->set_pixel(x, y, Rgbai{paint_color});
391  }
392  };
393 
394  ASSERT(image);
395 
396  float x0 = from.x;
397  float y0 = from.y;
398  float x1 = to.x;
399  float y1 = to.y;
400 
401  using std::swap;
402 
403  auto steep = abs(y1 - y0) > abs(x1 - x0);
404 
405  if(steep)
406  {
407  swap(x0, y0);
408  swap(x1, y1);
409  }
410  if(x0 > x1)
411  {
412  swap(x0, x1);
413  swap(y0, y1);
414  }
415 
416  float dx = x1 - x0;
417  float dy = y1 - y0;
418  float gradient = dy / dx;
419  if(dx == 0.0f)
420  {
421  gradient = 1.0f;
422  }
423 
424  // handle first endpoint
425  auto xend = round(x0);
426  auto yend = y0 + gradient * (c_int_to_float(xend) - x0);
427  auto xgap = rfpart(x0 + 0.5f);
428  auto xpxl1 = xend; // this will be used in the main loop
429  auto ypxl1 = ipart(yend);
430  if(steep)
431  {
432  plot(ypxl1, xpxl1, rfpart(yend) * xgap);
433  plot(ypxl1+1, xpxl1, fpart(yend) * xgap);
434  }
435  else
436  {
437  plot(xpxl1, ypxl1 , rfpart(yend) * xgap);
438  plot(xpxl1, ypxl1+1, fpart(yend) * xgap);
439  }
440  auto intery = yend + gradient; // first y-intersection for the main loop
441 
442  // handle second endpoint
443  xend = round(x1);
444  yend = y1 + gradient * (c_int_to_float(xend) - x1);
445  xgap = fpart(x1 + 0.5f);
446  auto xpxl2 = xend; //this will be used in the main loop
447  auto ypxl2 = ipart(yend);
448 
449  if(steep)
450  {
451  plot(ypxl2 , xpxl2, rfpart(yend) * xgap);
452  plot(ypxl2+1, xpxl2, fpart(yend) * xgap);
453  }
454  else
455  {
456  plot(xpxl2, ypxl2, rfpart(yend) * xgap);
457  plot(xpxl2, ypxl2+1, fpart(yend) * xgap);
458  }
459 
460  // main loop
461  if(steep)
462  {
463  for(auto x=xpxl1 + 1; x<=xpxl2 - 1; x+=1)
464  {
465  plot(ipart(intery) , x, rfpart(intery));
466  plot(ipart(intery)+1, x, fpart(intery));
467  intery = intery + gradient;
468  }
469  }
470  else
471  {
472  for(auto x = xpxl1 + 1; x<xpxl2 - 1; x+=1)
473  {
474  plot(x, ipart(intery), rfpart(intery));
475  plot(x, ipart(intery)+1, fpart(intery));
476  intery = intery + gradient;
477  }
478  }
479  }
480 
481  Rgba tint_color(const Rgba& c, const Rgb& tint)
482  {
483  return {{c.r * tint.r, c.g * tint.g, c.b * tint.b}, c.a};
484  }
485 
486  Rgbai tint_color(const Rgbai& c, const Rgbai& tint)
487  {
488  return to_rgbai(tint_color(to_rgba(c), to_rgb(tint)));
489  }
490 
491  void
493  (
494  Image* dst,
495  const vec2i& p,
496  const Image& src,
497  const Rgbai& tint
498  )
499  {
500  for(int y=0; y<src.height; y+=1)
501  {
502  for(int x=0; x<src.width; x+=1)
503  {
504  const auto dx = p.x + x;
505  const auto dy = p.y + y;
506  if(dx >= dst->width) { continue; }
507  if(dy >= dst->height) { continue; }
508  const auto dst_color = dst->get_pixel(dx, dy);
509  const auto src_color = src.get_pixel(x, y);
510  const auto tinted_color = tint_color(src_color, tint);
511  const auto result_color = blend(tinted_color, dst_color);
512  dst->set_pixel(dx, dy, result_color);
513  }
514  }
515  }
516 
517  void
519  (
520  Image* image,
521  const vec2i& start_pos,
522  const std::string& text,
523  const Rgbai& color,
524  const LoadedFont& font
525  )
526  {
527  ASSERT(image);
528 
529  vec2i pos = start_pos;
530  calc_utf8_to_codepoints(text, [&](int cp)
531  {
532  if(cp == '\n')
533  {
534  pos.x = start_pos.x;
535  // todo(Gustav): font needs lineheight...
536  pos.y -= font.line_height;
537  }
538  else
539  {
540  const auto glyph_found = font.codepoint_to_glyph.find(cp);
541  if(glyph_found != font.codepoint_to_glyph.end())
542  {
543  const auto& glyph = glyph_found->second;
544  blend_image(image, pos, glyph.image, color);
545  pos.x += glyph.advance;
546  }
547  }
548  });
549  }
550 
551  void
553  (
554  Image* dest_image,
555  const vec2i& position,
556  const Image& source_image,
557  BlendMode blend_mode,
558  PixelsOutside clip
559  )
560  {
561  ASSERT(dest_image);
562 
563  for(int y = 0; y < source_image.height; ++y)
564  {
565  for(int x = 0; x < source_image.width; ++x)
566  {
567  const auto dest_x = position.x + x;
568  const auto dest_y = position.y + y;
569  if
570  (
571  clip == PixelsOutside::discard &&
572  is_within(dest_image->get_indices(), vec2i(dest_x, dest_y)) == false
573  )
574  {
575  // nop
576  }
577  else
578  {
579  const auto top = source_image.get_pixel(x, y);
580  const auto bottom = dest_image->get_pixel(dest_x, dest_y);
581  const auto color = blend(top, bottom, blend_mode);
582  dest_image->set_pixel(dest_x, dest_y, color);
583  }
584  }
585  }
586  }
587 
588 
589  void
591  (
592  Image* image,
593  const vec2f& a,
594  const vec2f& b,
595  const vec2f& c,
596  const Rgbai& color
597  )
598  {
599  const auto [minf, maxf] = find_min_max<vec2f, std::vector<vec2f> >
600  (
601  {a, b, c},
602  [](const vec2f& lhs, const vec2f& rhs) -> vec2f
603  {
604  return {std::min(lhs.x, rhs.x), std::min(lhs.y, rhs.y)};
605  },
606  [](const vec2f& lhs, const vec2f& rhs) -> vec2f
607  {
608  return {std::max(lhs.x, rhs.x), std::max(lhs.y, rhs.y)};
609  }
610  );
611 
612  const auto min = minf.to_i();
613  const auto max = maxf.to_i();
614 
615  for(int y=min.y; y<=max.y; y+=1)
616  {
617  for(int x=min.x; x<=max.x; x+=1)
618  {
619  const bool valid_x = is_within_inclusive_as_int
620  (
621  0, x, image->width - 1
622  );
623  const bool valid_y = is_within_inclusive_as_int
624  (
625  0, y, image->height - 1
626  );
627  if(valid_x && valid_y)
628  {
629  const auto inside_triangle = is_point_in_triangle
630  (
631  a,
632  b,
633  c,
634  vec2f{static_cast<float>(x), static_cast<float>(y)}
635  );
636  if(inside_triangle)
637  {
638  image->set_pixel(x, y, color);
639  }
640  }
641  }
642  }
643  }
644 
645 
646  void
648  (
649  Image* image,
650  const vec2f& from,
651  const vec2f& to,
652  const Rgbai& color,
653  float size
654  )
655  {
656  // based on code from https://www.codeproject.com/Questions/125049/Draw-an-arrow-with-big-cap
657  // todo(Gustav): this is too complicated, and hard to customize, different pointy arrows that ain't 90 degrees
658  // there must be a better way to do it
659  // also generalize it so we can have arrows in dvg/dummper code too
660  const vec2f arrow_point = to;
661 
662  const auto arrow_length = sqrt(square(abs(from.x - to.x)) +
663  square(abs(from.y - to.y)));
664 
665  const auto arrow_angle = atan2(abs(from.y - to.y), abs(from.x - to.x));
666  const auto angle_b = atan2((3 * size), (arrow_length - (3 * size)));
667  const auto secondary_length = (3 * size) / sin(angle_b);
668 
669  auto angle_c = Angle::from_degrees(90) - arrow_angle - angle_b;
670  const auto arrow_point_left_x = from.x > to.x
671  ? from.x - (sin(angle_c) * secondary_length)
672  : (sin(angle_c) * secondary_length) + from.x
673  ;
674  const auto arrow_point_left_y = from.y > to.y
675  ? from.y - (cos(angle_c) * secondary_length)
676  : (cos(angle_c) * secondary_length) + from.y
677  ;
678  const auto arrow_point_left = vec2f
679  {
680  arrow_point_left_x,
681  arrow_point_left_y
682  };
683 
684  //move to the right point
685  angle_c = arrow_angle - angle_b;
686 
687  const auto arrow_point_right_x = from.x > to.x
688  ? from.x - (cos(angle_c) * secondary_length)
689  : (cos(angle_c) * secondary_length) + from.x
690  ;
691  const auto arrow_point_right_y = from.y > to.y
692  ? from.y - (sin(angle_c) * secondary_length)
693  : (sin(angle_c) * secondary_length) + from.y
694  ;
695  const auto arrow_point_right = vec2f
696  {
697  arrow_point_right_x,
698  arrow_point_right_y
699  };
700 
701  // line
702  draw_line_antialiased(image, to_rgb(color), from, to);
703 
704  // wings
705  // DrawLineAntialiased(image, rgb(color), arrowPoint, arrowPointLeft);
706  // DrawLineAntialiased(image, rgb(color), arrowPoint, arrowPointRight);
707  // DrawLineAntialiased(image, rgb(color), arrowPointLeft, arrowPointRight);
708 
709  fill_triangle(image, arrow_point_left, arrow_point, arrow_point_right, color);
710  }
711 
712 
713 }
714 
#define ASSERT(x)
Definition: assert.h:29
constexpr unit3f in
Definition: vec3.h:135
Rgb blend(const Rgb &top, const Rgb &bottom, BlendMode mode)
Definition: rgb_blend.cc:402
void fill_triangle(Image *image, const vec2f &a, const vec2f &b, const vec2f &c, const Rgbai &color)
Definition: image_draw.cc:591
void draw_line_antialiased(Image *image, const Rgb &color, const vec2f &from, const vec2f &to)
Definition: image_draw.cc:362
void clear(Image *image, const Rgbai &color)
Definition: image_draw.cc:32
void draw_text(Image *image, const vec2i &start_pos, const std::string &text, const Rgbai &color, const LoadedFont &font)
Definition: image_draw.cc:519
void draw_circle(Image *image, const Rgb &color, const vec2i &center, float radius, float softness, float inner)
Definition: image_draw.cc:175
void draw_arrow(Image *image, const vec2f &from, const vec2f &to, const Rgbai &color, float size)
Definition: image_draw.cc:648
void blend_image(Image *dst, const vec2i &p, const Image &src, const Rgbai &tint)
Definition: image_draw.cc:493
void fill_poly(Image *image, const Rgbai &color, const std::vector< vec2f > &poly)
Definition: image_draw.cc:146
bool calc_utf8_to_codepoints(const TString &string, TOnCodepointFunc on_codepoint)
Definition: utf8.h:11
void draw_line_fast(Image *image, const Rgbai &color, const vec2i &from, const vec2i &to)
Definition: image_draw.cc:254
Recti on_whole_image(const Image &image)
Definition: image_draw.cc:21
Rgba tint_color(const Rgba &c, const Rgb &tint)
Definition: image_draw.cc:481
bool is_point_in_triangle(const vec2f &a, const vec2f &b, const vec2f &c, const vec2f &p)
void draw_line_antialiased(Image *image, const Rgb &color, const vec2i &from, const vec2i &to)
Definition: image_draw.cc:342
void draw_square(Image *image, const Rgbai &color, int x, int y, int size)
Definition: image_draw.cc:67
void paste_image(Image *dest_image, const vec2i &position, const Image &source_image, BlendMode blend_mode, PixelsOutside clip)
Definition: image_draw.cc:553
void draw_rect(Image *image, const Rgbai &color, const Recti &rect)
Definition: image_draw.cc:39
constexpr ShaderAttribute color
float sqrt(float r)
Definition: numeric.cc:101
Rgbai to_rgbai(const Rgba &c)
Definition: rgb.cc:395
Angle atan2(float y, float x)
Definition: angle.cc:107
constexpr int c_float_to_int(float f)
Definition: cint.h:36
bool is_within_inclusive_as_int(int min, int c, int max)
Definition: numeric.cc:116
float to01(const Range< T > &range, T value)
Definition: range.h:84
Range< T > make_range(T min, T max)
Definition: range.h:39
Rgb to_rgb(const Rgbi &c)
Definition: rgb.cc:229
constexpr float abs(float r)
Definition: numeric.h:8
float square(float r)
Definition: numeric.cc:94
float round(float num, float gran)
Rounds a value to the nearest nice value.
Definition: numeric.cc:140
constexpr float c_int_to_float(int i)
Definition: cint.h:50
float cos(const Angle &ang)
Definition: angle.cc:68
size2f min(const size2f lhs, const size2f rhs)
Definition: size2.cc:140
Rgba to_rgba(const Rgbai &c)
Definition: rgb.cc:381
Rgb lerp_rgb(const Rgb &from, float v, const Rgb &to)
Definition: rgb.cc:410
float floor(float v)
Definition: numeric.cc:35
float sin(const Angle &ang)
Definition: angle.cc:61
bool is_within(const Range< T > &range, T value)
Definition: range.h:108
size2f max(const size2f lhs, const size2f rhs)
Definition: size2.cc:149
int ceil_to_int(float v)
Definition: numeric.cc:56
int floor_to_int(float v)
Definition: numeric.cc:49
constexpr static Angle from_degrees(float degrees)
Definition: angle.h:16
Definition: rect.h:27
static Rectf from_left_right_bottom_top(float left_side, float right_side, float bottom_side, float top_side)
Definition: rect.cc:39
vec2i get_top_right() const
Definition: rect.cc:928
vec2i get_bottom_left() const
Definition: rect.cc:593
vec2i get_top_left() const
Definition: rect.cc:922
static Recti from_top_left_width_height(const vec2i &topleft, int width, int height)
Definition: rect.cc:561
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:143
Definition: rgb.h:45
Recti get_indices() const
Definition: image.cc:168
void set_pixel(int x, int y, const Rgbai &color)
Definition: image.cc:104
Rgbai get_pixel(int x, int y) const
Definition: image.cc:137
int height
Definition: image.h:33
std::map< int, LoadedGlyph > codepoint_to_glyph
Definition: loadedfont.h:54
Definition: vec2.h:33
static vec2f from_to(const vec2f &from, const vec2f &to)
Definition: vec2.cc:83
float x
Definition: vec2.h:34
float y
Definition: vec2.h:35
Definition: vec2.h:72
int y
Definition: vec2.h:74
vec2f to_f() const
Definition: vec2.cc:137
int x
Definition: vec2.h:73