Blender  V3.3
COM_MemoryBuffer.h
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2011 Blender Foundation. */
3 
4 #pragma once
5 
6 #include "COM_BufferArea.h"
7 #include "COM_BufferRange.h"
8 #include "COM_BuffersIterator.h"
9 #include "COM_Enums.h"
10 
11 #include "BLI_math_interp.h"
12 #include "BLI_rect.h"
13 
14 #include "IMB_colormanagement.h"
15 
16 struct ImBuf;
17 
18 namespace blender::compositor {
19 
24 enum class MemoryBufferState {
27  Default = 0,
29  Temporary = 6,
30 };
31 
32 enum class MemoryBufferExtend {
33  Clip,
34  Extend,
35  Repeat,
36 };
37 
38 class MemoryProxy;
39 
43 class MemoryBuffer {
44  public:
53 
62 
63  private:
67  MemoryProxy *memory_proxy_;
68 
72  DataType datatype_;
73 
77  rcti rect_;
78 
82  MemoryBufferState state_;
83 
87  float *buffer_;
88 
93  uint8_t num_channels_;
94 
98  bool is_a_single_elem_;
99 
103  bool owns_data_;
104 
106  int to_positive_x_stride_;
107 
109  int to_positive_y_stride_;
110 
111  public:
115  MemoryBuffer(MemoryProxy *memory_proxy, const rcti &rect, MemoryBufferState state);
116 
120  MemoryBuffer(DataType data_type, const rcti &rect, bool is_a_single_elem = false);
121 
126  MemoryBuffer(
127  float *buffer, int num_channels, int width, int height, bool is_a_single_elem = false);
128 
133  MemoryBuffer(float *buffer, int num_channels, const rcti &rect, bool is_a_single_elem = false);
134 
138  MemoryBuffer(const MemoryBuffer &src);
139 
143  ~MemoryBuffer();
144 
149  bool is_a_single_elem() const
150  {
151  return is_a_single_elem_;
152  }
153 
154  float &operator[](int index)
155  {
156  BLI_assert(is_a_single_elem_ ? index < num_channels_ :
157  index < get_coords_offset(get_width(), get_height()));
158  return buffer_[index];
159  }
160 
161  const float &operator[](int index) const
162  {
163  BLI_assert(is_a_single_elem_ ? index < num_channels_ :
164  index < get_coords_offset(get_width(), get_height()));
165  return buffer_[index];
166  }
167 
171  intptr_t get_coords_offset(int x, int y) const
172  {
173  return ((intptr_t)y - rect_.ymin) * row_stride + ((intptr_t)x - rect_.xmin) * elem_stride;
174  }
175 
179  float *get_elem(int x, int y)
180  {
181  BLI_assert(has_coords(x, y));
182  return buffer_ + get_coords_offset(x, y);
183  }
184 
188  const float *get_elem(int x, int y) const
189  {
190  BLI_assert(has_coords(x, y));
191  return buffer_ + get_coords_offset(x, y);
192  }
193 
194  void read_elem(int x, int y, float *out) const
195  {
196  memcpy(out, get_elem(x, y), get_elem_bytes_len());
197  }
198 
199  void read_elem_checked(int x, int y, float *out) const
200  {
201  if (!has_coords(x, y)) {
202  clear_elem(out);
203  }
204  else {
205  read_elem(x, y, out);
206  }
207  }
208 
209  void read_elem_checked(float x, float y, float *out) const
210  {
211  read_elem_checked(floor_x(x), floor_y(y), out);
212  }
213 
214  void read_elem_bilinear(float x, float y, float *out) const
215  {
216  /* Only clear past +/-1 borders to be able to smooth edges. */
217  if (x <= rect_.xmin - 1.0f || x >= rect_.xmax || y <= rect_.ymin - 1.0f || y >= rect_.ymax) {
218  clear_elem(out);
219  return;
220  }
221 
222  if (is_a_single_elem_) {
223  if (x >= rect_.xmin && x < rect_.xmax - 1.0f && y >= rect_.ymin && y < rect_.ymax - 1.0f) {
224  memcpy(out, buffer_, get_elem_bytes_len());
225  return;
226  }
227 
228  /* Do sampling at borders to smooth edges. */
229  const float last_x = get_width() - 1.0f;
230  const float rel_x = get_relative_x(x);
231  float single_x = 0.0f;
232  if (rel_x < 0.0f) {
233  single_x = rel_x;
234  }
235  else if (rel_x > last_x) {
236  single_x = rel_x - last_x;
237  }
238 
239  const float last_y = get_height() - 1.0f;
240  const float rel_y = get_relative_y(y);
241  float single_y = 0.0f;
242  if (rel_y < 0.0f) {
243  single_y = rel_y;
244  }
245  else if (rel_y > last_y) {
246  single_y = rel_y - last_y;
247  }
248 
249  BLI_bilinear_interpolation_fl(buffer_, out, 1, 1, num_channels_, single_x, single_y);
250  return;
251  }
252 
254  out,
255  get_width(),
256  get_height(),
257  num_channels_,
258  get_relative_x(x),
259  get_relative_y(y));
260  }
261 
262  void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const
263  {
264  switch (sampler) {
267  break;
270  /* No bicubic. Current implementation produces fuzzy results. */
272  break;
273  }
274  }
275 
276  void read_elem_filtered(float x, float y, float dx[2], float dy[2], float *out) const;
277 
281  float &get_value(int x, int y, int channel)
282  {
283  BLI_assert(has_coords(x, y) && channel >= 0 && channel < num_channels_);
284  return buffer_[get_coords_offset(x, y) + channel];
285  }
286 
290  const float &get_value(int x, int y, int channel) const
291  {
292  BLI_assert(has_coords(x, y) && channel >= 0 && channel < num_channels_);
293  return buffer_[get_coords_offset(x, y) + channel];
294  }
295 
299  const float *get_row_end(int y) const
300  {
301  BLI_assert(has_y(y));
302  return buffer_ + (is_a_single_elem() ? num_channels_ : get_coords_offset(get_width(), y));
303  }
304 
309  int get_memory_width() const
310  {
311  return is_a_single_elem() ? 1 : get_width();
312  }
313 
318  int get_memory_height() const
319  {
320  return is_a_single_elem() ? 1 : get_height();
321  }
322 
324  {
325  return num_channels_;
326  }
327 
329  {
330  return num_channels_ * sizeof(float);
331  }
332 
337  {
338  return BufferRange<float>(buffer_, 0, buffer_len(), elem_stride);
339  }
340 
342  {
343  return BufferRange<const float>(buffer_, 0, buffer_len(), elem_stride);
344  }
345 
347  {
348  return BufferArea<float>(buffer_, get_width(), area, elem_stride);
349  }
350 
352  {
353  return BufferArea<const float>(buffer_, get_width(), area, elem_stride);
354  }
355 
358 
363  float *get_buffer()
364  {
365  return buffer_;
366  }
367 
369  {
370  owns_data_ = false;
371  return buffer_;
372  }
373 
378  MemoryBuffer *inflate() const;
379 
380  inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y)
381  {
382  const int w = get_width();
383  const int h = get_height();
384  x = x - rect_.xmin;
385  y = y - rect_.ymin;
386 
387  switch (extend_x) {
389  break;
391  if (x < 0) {
392  x = 0;
393  }
394  if (x >= w) {
395  x = w - 1;
396  }
397  break;
399  x %= w;
400  if (x < 0) {
401  x += w;
402  }
403  break;
404  }
405 
406  switch (extend_y) {
408  break;
410  if (y < 0) {
411  y = 0;
412  }
413  if (y >= h) {
414  y = h - 1;
415  }
416  break;
418  y %= h;
419  if (y < 0) {
420  y += h;
421  }
422  break;
423  }
424 
425  x = x + rect_.xmin;
426  y = y + rect_.ymin;
427  }
428 
429  inline void wrap_pixel(float &x,
430  float &y,
431  MemoryBufferExtend extend_x,
432  MemoryBufferExtend extend_y) const
433  {
434  const float w = (float)get_width();
435  const float h = (float)get_height();
436  x = x - rect_.xmin;
437  y = y - rect_.ymin;
438 
439  switch (extend_x) {
441  break;
443  if (x < 0) {
444  x = 0.0f;
445  }
446  if (x >= w) {
447  x = w - 1;
448  }
449  break;
451  x = fmodf(x, w);
452  if (x < 0.0f) {
453  x += w;
454  }
455  break;
456  }
457 
458  switch (extend_y) {
460  break;
462  if (y < 0) {
463  y = 0.0f;
464  }
465  if (y >= h) {
466  y = h - 1;
467  }
468  break;
470  y = fmodf(y, h);
471  if (y < 0.0f) {
472  y += h;
473  }
474  break;
475  }
476 
477  x = x + rect_.xmin;
478  y = y + rect_.ymin;
479  }
480 
481  /* TODO(manzanilla): to be removed with tiled implementation. For applying #MemoryBufferExtend
482  * use #wrap_pixel. */
483  inline void read(float *result,
484  int x,
485  int y,
488  {
489  bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < rect_.xmin || x >= rect_.xmax));
490  bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < rect_.ymin || y >= rect_.ymax));
491  if (clip_x || clip_y) {
492  /* clip result outside rect is zero */
493  memset(result, 0, num_channels_ * sizeof(float));
494  }
495  else {
496  int u = x;
497  int v = y;
498  this->wrap_pixel(u, v, extend_x, extend_y);
499  const int offset = get_coords_offset(u, v);
500  float *buffer = &buffer_[offset];
501  memcpy(result, buffer, sizeof(float) * num_channels_);
502  }
503  }
504 
505  /* TODO(manzanilla): to be removed with tiled implementation. */
506  inline void read_no_check(float *result,
507  int x,
508  int y,
511  {
512  int u = x;
513  int v = y;
514 
515  this->wrap_pixel(u, v, extend_x, extend_y);
516  const int offset = get_coords_offset(u, v);
517 
518  BLI_assert(offset >= 0);
519  BLI_assert(offset < this->buffer_len() * num_channels_);
520  BLI_assert(!(extend_x == MemoryBufferExtend::Clip && (u < rect_.xmin || u >= rect_.xmax)) &&
521  !(extend_y == MemoryBufferExtend::Clip && (v < rect_.ymin || v >= rect_.ymax)));
522  float *buffer = &buffer_[offset];
523  memcpy(result, buffer, sizeof(float) * num_channels_);
524  }
525 
526  void write_pixel(int x, int y, const float color[4]);
527  void add_pixel(int x, int y, const float color[4]);
528  inline void read_bilinear(float *result,
529  float x,
530  float y,
533  {
534  float u = x;
535  float v = y;
536  this->wrap_pixel(u, v, extend_x, extend_y);
537  if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= get_width())) ||
538  (extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= get_height()))) {
539  copy_vn_fl(result, num_channels_, 0.0f);
540  return;
541  }
542  if (is_a_single_elem_) {
543  memcpy(result, buffer_, sizeof(float) * num_channels_);
544  }
545  else {
547  result,
548  get_width(),
549  get_height(),
550  num_channels_,
551  u,
552  v,
553  extend_x == MemoryBufferExtend::Repeat,
554  extend_y == MemoryBufferExtend::Repeat);
555  }
556  }
557 
558  void readEWA(float *result, const float uv[2], const float derivatives[2][2]);
559 
563  inline bool is_temporarily() const
564  {
565  return state_ == MemoryBufferState::Temporary;
566  }
567 
571  void apply_processor(ColormanageProcessor &processor, const rcti area);
572 
573  void copy_from(const MemoryBuffer *src, const rcti &area);
574  void copy_from(const MemoryBuffer *src, const rcti &area, int to_x, int to_y);
575  void copy_from(const MemoryBuffer *src,
576  const rcti &area,
577  int channel_offset,
578  int elem_size,
579  int to_channel_offset);
580  void copy_from(const MemoryBuffer *src,
581  const rcti &area,
582  int channel_offset,
583  int elem_size,
584  int to_x,
585  int to_y,
586  int to_channel_offset);
587  void copy_from(const uchar *src, const rcti &area);
588  void copy_from(const uchar *src,
589  const rcti &area,
590  int channel_offset,
591  int elem_size,
592  int elem_stride,
593  int row_stride,
594  int to_channel_offset);
595  void copy_from(const uchar *src,
596  const rcti &area,
597  int channel_offset,
598  int elem_size,
599  int elem_stride,
600  int row_stride,
601  int to_x,
602  int to_y,
603  int to_channel_offset);
604  void copy_from(const struct ImBuf *src, const rcti &area, bool ensure_linear_space = false);
605  void copy_from(const struct ImBuf *src,
606  const rcti &area,
607  int channel_offset,
608  int elem_size,
609  int to_channel_offset,
610  bool ensure_linear_space = false);
611  void copy_from(const struct ImBuf *src,
612  const rcti &src_area,
613  int channel_offset,
614  int elem_size,
615  int to_x,
616  int to_y,
617  int to_channel_offset,
618  bool ensure_linear_space = false);
619 
620  void fill(const rcti &area, const float *value);
621  void fill(const rcti &area, int channel_offset, const float *value, int value_size);
629  void fill_from(const MemoryBuffer &src);
630 
634  const rcti &get_rect() const
635  {
636  return rect_;
637  }
638 
642  const int get_width() const
643  {
644  return BLI_rcti_size_x(&rect_);
645  }
646 
650  const int get_height() const
651  {
652  return BLI_rcti_size_y(&rect_);
653  }
654 
658  void clear();
659 
660  float get_max_value() const;
661  float get_max_value(const rcti &rect) const;
662 
663  private:
664  void set_strides();
665  const int buffer_len() const
666  {
667  return get_memory_width() * get_memory_height();
668  }
669 
670  void clear_elem(float *out) const
671  {
672  memset(out, 0, num_channels_ * sizeof(float));
673  }
674 
675  template<typename T> T get_relative_x(T x) const
676  {
677  return x - rect_.xmin;
678  }
679 
680  template<typename T> T get_relative_y(T y) const
681  {
682  return y - rect_.ymin;
683  }
684 
685  template<typename T> bool has_coords(T x, T y) const
686  {
687  return has_x(x) && has_y(y);
688  }
689 
690  template<typename T> bool has_x(T x) const
691  {
692  return x >= rect_.xmin && x < rect_.xmax;
693  }
694 
695  template<typename T> bool has_y(T y) const
696  {
697  return y >= rect_.ymin && y < rect_.ymax;
698  }
699 
700  /* Fast `floor(..)` functions. The caller should check result is within buffer bounds.
701  * It `ceil(..)` in near cases and when given coordinate
702  * is negative and less than buffer rect `min - 1`. */
703  int floor_x(float x) const
704  {
705  return (int)(x + to_positive_x_stride_) - to_positive_x_stride_;
706  }
707 
708  int floor_y(float y) const
709  {
710  return (int)(y + to_positive_y_stride_) - to_positive_y_stride_;
711  }
712 
713  void copy_single_elem_from(const MemoryBuffer *src,
714  int channel_offset,
715  int elem_size,
716  int to_channel_offset);
717  void copy_rows_from(const MemoryBuffer *src, const rcti &src_area, int to_x, int to_y);
718  void copy_elems_from(const MemoryBuffer *src,
719  const rcti &area,
720  int channel_offset,
721  int elem_size,
722  int to_x,
723  int to_y,
724  int to_channel_offset);
725 
726 #ifdef WITH_CXX_GUARDEDALLOC
727  MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryBuffer")
728 #endif
729 };
730 
731 } // namespace blender::compositor
typedef float(TangentPoint)[2]
#define BLI_assert(a)
Definition: BLI_assert.h:46
void BLI_bilinear_interpolation_fl(const float *buffer, float *output, int width, int height, int components, float u, float v)
Definition: math_interp.c:445
void BLI_bilinear_interpolation_wrap_fl(const float *buffer, float *output, int width, int height, int components, float u, float v, bool wrap_x, bool wrap_y)
Definition: math_interp.c:464
void copy_vn_fl(float *array_tar, int size, float val)
Definition: math_vector.c:1259
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition: BLI_rect.h:190
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition: BLI_rect.h:186
unsigned char uchar
Definition: BLI_sys_types.h:70
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition: btQuadWord.h:119
a MemoryBuffer contains access to the data of a chunk
void read(float *result, int x, int y, MemoryBufferExtend extend_x=MemoryBufferExtend::Clip, MemoryBufferExtend extend_y=MemoryBufferExtend::Clip)
void copy_from(const struct ImBuf *src, const rcti &area, int channel_offset, int elem_size, int to_channel_offset, bool ensure_linear_space=false)
void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y)
MemoryBuffer(MemoryProxy *memory_proxy, const rcti &rect, MemoryBufferState state)
construct new temporarily MemoryBuffer for an area
void read_elem_checked(int x, int y, float *out) const
bool is_temporarily() const
is this MemoryBuffer a temporarily buffer (based on an area, not on a chunk)
BufferArea< const float > get_buffer_area(const rcti &area) const
BufferRange< const float > as_range() const
const float & get_value(int x, int y, int channel) const
void copy_from(const MemoryBuffer *src, const rcti &area)
const rcti & get_rect() const
get the rect of this MemoryBuffer
void copy_from(const struct ImBuf *src, const rcti &src_area, int channel_offset, int elem_size, int to_x, int to_y, int to_channel_offset, bool ensure_linear_space=false)
void read_elem_checked(float x, float y, float *out) const
const int get_width() const
get the width of this MemoryBuffer
intptr_t get_coords_offset(int x, int y) const
const int get_height() const
get the height of this MemoryBuffer
const float * get_row_end(int y) const
void write_pixel(int x, int y, const float color[4])
void read_elem_bilinear(float x, float y, float *out) const
BufferArea< float > get_buffer_area(const rcti &area)
const float & operator[](int index) const
const float * get_elem(int x, int y) const
float * get_buffer()
get the data of this MemoryBuffer
void read_elem(int x, int y, float *out) const
void fill(const rcti &area, const float *value)
void fill_from(const MemoryBuffer &src)
add the content from other_buffer to this MemoryBuffer
void wrap_pixel(float &x, float &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) const
void read_bilinear(float *result, float x, float y, MemoryBufferExtend extend_x=MemoryBufferExtend::Clip, MemoryBufferExtend extend_y=MemoryBufferExtend::Clip) const
void readEWA(float *result, const float uv[2], const float derivatives[2][2])
float & get_value(int x, int y, int channel)
void apply_processor(ColormanageProcessor &processor, const rcti area)
Apply a color processor on the given area.
void add_pixel(int x, int y, const float color[4])
void copy_from(const struct ImBuf *src, const rcti &area, bool ensure_linear_space=false)
void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const
void read_no_check(float *result, int x, int y, MemoryBufferExtend extend_x=MemoryBufferExtend::Clip, MemoryBufferExtend extend_y=MemoryBufferExtend::Clip)
BuffersIterator< float > iterate_with(Span< MemoryBuffer * > inputs)
void clear()
clear the buffer. Make all pixels black transparent.
void read_elem_filtered(float x, float y, float dx[2], float dy[2], float *out) const
A MemoryProxy is a unique identifier for a memory buffer. A single MemoryProxy is used among all chun...
SyclQueue void void * src
depth_tx sampler(1, ImageType::FLOAT_2D, "combined_tx") .sampler(2
MemoryBufferState
state of a memory buffer
@ Temporary
chunk is consolidated from other chunks. special state.
@ Default
memory has been allocated on creator device and CPU machine, but kernel has not been executed
DataType
possible data types for sockets
Definition: COM_defines.h:30
ccl_global float * buffer
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
const int state
#define T
#define fmodf(x, y)
Definition: metal/compat.h:230
static void area(int d1, int d2, int e1, int e2, float weights[2])
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
static const pxr::TfToken out("out", pxr::TfToken::Immortal)
static bNodeSocketTemplate inputs[]
_W64 int intptr_t
Definition: stdint.h:118
unsigned char uint8_t
Definition: stdint.h:78
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63