Fix T88262: Curve to mesh crash with vector last segment
[blender.git] / source / blender / compositor / intern / COM_MemoryBuffer.h
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2011, Blender Foundation.
17  */
18
19 #pragma once
20
21 #include "COM_ExecutionGroup.h"
22 #include "COM_MemoryProxy.h"
23
24 #include "BLI_math.h"
25 #include "BLI_rect.h"
26
27 namespace blender::compositor {
28
29 /**
30  * \brief state of a memory buffer
31  * \ingroup Memory
32  */
33 enum class MemoryBufferState {
34   /** \brief memory has been allocated on creator device and CPU machine,
35    * but kernel has not been executed */
36   Default = 0,
37   /** \brief chunk is consolidated from other chunks. special state.*/
38   Temporary = 6,
39 };
40
41 enum class MemoryBufferExtend {
42   Clip,
43   Extend,
44   Repeat,
45 };
46
47 class MemoryProxy;
48
49 /**
50  * \brief a MemoryBuffer contains access to the data of a chunk
51  */
52 class MemoryBuffer {
53  public:
54   /**
55    * Offset between elements.
56    *
57    * Should always be used for the x dimension when calculating buffer offsets.
58    * It will be 0 when is_a_single_elem=true.
59    * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
60    */
61   int elem_stride;
62
63   /**
64    * Offset between rows.
65    *
66    * Should always be used for the y dimension when calculating buffer offsets.
67    * It will be 0 when is_a_single_elem=true.
68    * e.g: buffer_index = y * buffer.row_stride + x * buffer.elem_stride
69    */
70   int row_stride;
71
72  private:
73   /**
74    * \brief proxy of the memory (same for all chunks in the same buffer)
75    */
76   MemoryProxy *m_memoryProxy;
77
78   /**
79    * \brief the type of buffer DataType::Value, DataType::Vector, DataType::Color
80    */
81   DataType m_datatype;
82
83   /**
84    * \brief region of this buffer inside relative to the MemoryProxy
85    */
86   rcti m_rect;
87
88   /**
89    * \brief state of the buffer
90    */
91   MemoryBufferState m_state;
92
93   /**
94    * \brief the actual float buffer/data
95    */
96   float *m_buffer;
97
98   /**
99    * \brief the number of channels of a single value in the buffer.
100    * For value buffers this is 1, vector 3 and color 4
101    */
102   uint8_t m_num_channels;
103
104   /**
105    * Whether buffer is a single element in memory.
106    */
107   bool m_is_a_single_elem;
108
109  public:
110   /**
111    * \brief construct new temporarily MemoryBuffer for an area
112    */
113   MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state);
114
115   /**
116    * \brief construct new temporarily MemoryBuffer for an area
117    */
118   MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false);
119
120   /**
121    * Copy constructor
122    */
123   MemoryBuffer(const MemoryBuffer &src);
124
125   /**
126    * \brief destructor
127    */
128   ~MemoryBuffer();
129
130   /**
131    * Whether buffer is a single element in memory independently of its resolution. True for set
132    * operations buffers.
133    */
134   bool is_a_single_elem() const
135   {
136     return m_is_a_single_elem;
137   }
138
139   float &operator[](int index)
140   {
141     BLI_assert(m_is_a_single_elem ? index < m_num_channels :
142                                     index < get_coords_offset(getWidth(), getHeight()));
143     return m_buffer[index];
144   }
145
146   const float &operator[](int index) const
147   {
148     BLI_assert(m_is_a_single_elem ? index < m_num_channels :
149                                     index < get_coords_offset(getWidth(), getHeight()));
150     return m_buffer[index];
151   }
152
153   /**
154    * Get offset needed to jump from buffer start to given coordinates.
155    */
156   int get_coords_offset(int x, int y) const
157   {
158     return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride;
159   }
160
161   /**
162    * Get buffer element at given coordinates.
163    */
164   float *get_elem(int x, int y)
165   {
166     BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
167     return m_buffer + get_coords_offset(x, y);
168   }
169
170   /**
171    * Get buffer element at given coordinates.
172    */
173   const float *get_elem(int x, int y) const
174   {
175     BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax);
176     return m_buffer + get_coords_offset(x, y);
177   }
178
179   /**
180    * Get channel value at given coordinates.
181    */
182   float &get_value(int x, int y, int channel)
183   {
184     BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
185                channel >= 0 && channel < m_num_channels);
186     return m_buffer[get_coords_offset(x, y) + channel];
187   }
188
189   /**
190    * Get channel value at given coordinates.
191    */
192   const float &get_value(int x, int y, int channel) const
193   {
194     BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax &&
195                channel >= 0 && channel < m_num_channels);
196     return m_buffer[get_coords_offset(x, y) + channel];
197   }
198
199   /**
200    * Get the buffer row end.
201    */
202   const float *get_row_end(int y) const
203   {
204     BLI_assert(y >= 0 && y < getHeight());
205     return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y));
206   }
207
208   /**
209    * Get the number of elements in memory for a row. For single element buffers it will always
210    * be 1.
211    */
212   int get_memory_width() const
213   {
214     return is_a_single_elem() ? 1 : getWidth();
215   }
216
217   /**
218    * Get number of elements in memory for a column. For single element buffers it will
219    * always be 1.
220    */
221   int get_memory_height() const
222   {
223     return is_a_single_elem() ? 1 : getHeight();
224   }
225
226   uint8_t get_num_channels()
227   {
228     return this->m_num_channels;
229   }
230
231   /**
232    * \brief get the data of this MemoryBuffer
233    * \note buffer should already be available in memory
234    */
235   float *getBuffer()
236   {
237     return this->m_buffer;
238   }
239
240   inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y)
241   {
242     const int w = getWidth();
243     const int h = getHeight();
244     x = x - m_rect.xmin;
245     y = y - m_rect.ymin;
246
247     switch (extend_x) {
248       case MemoryBufferExtend::Clip:
249         break;
250       case MemoryBufferExtend::Extend:
251         if (x < 0) {
252           x = 0;
253         }
254         if (x >= w) {
255           x = w;
256         }
257         break;
258       case MemoryBufferExtend::Repeat:
259         x = (x >= 0.0f ? (x % w) : (x % w) + w);
260         break;
261     }
262
263     switch (extend_y) {
264       case MemoryBufferExtend::Clip:
265         break;
266       case MemoryBufferExtend::Extend:
267         if (y < 0) {
268           y = 0;
269         }
270         if (y >= h) {
271           y = h;
272         }
273         break;
274       case MemoryBufferExtend::Repeat:
275         y = (y >= 0.0f ? (y % h) : (y % h) + h);
276         break;
277     }
278   }
279
280   inline void wrap_pixel(float &x,
281                          float &y,
282                          MemoryBufferExtend extend_x,
283                          MemoryBufferExtend extend_y)
284   {
285     const float w = (float)getWidth();
286     const float h = (float)getHeight();
287     x = x - m_rect.xmin;
288     y = y - m_rect.ymin;
289
290     switch (extend_x) {
291       case MemoryBufferExtend::Clip:
292         break;
293       case MemoryBufferExtend::Extend:
294         if (x < 0) {
295           x = 0.0f;
296         }
297         if (x >= w) {
298           x = w;
299         }
300         break;
301       case MemoryBufferExtend::Repeat:
302         x = fmodf(x, w);
303         break;
304     }
305
306     switch (extend_y) {
307       case MemoryBufferExtend::Clip:
308         break;
309       case MemoryBufferExtend::Extend:
310         if (y < 0) {
311           y = 0.0f;
312         }
313         if (y >= h) {
314           y = h;
315         }
316         break;
317       case MemoryBufferExtend::Repeat:
318         y = fmodf(y, h);
319         break;
320     }
321   }
322
323   inline void read(float *result,
324                    int x,
325                    int y,
326                    MemoryBufferExtend extend_x = MemoryBufferExtend::Clip,
327                    MemoryBufferExtend extend_y = MemoryBufferExtend::Clip)
328   {
329     bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < m_rect.xmin || x >= m_rect.xmax));
330     bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < m_rect.ymin || y >= m_rect.ymax));
331     if (clip_x || clip_y) {
332       /* clip result outside rect is zero */
333       memset(result, 0, this->m_num_channels * sizeof(float));
334     }
335     else {
336       int u = x;
337       int v = y;
338       this->wrap_pixel(u, v, extend_x, extend_y);
339       const int offset = get_coords_offset(u, v);
340       float *buffer = &this->m_buffer[offset];
341       memcpy(result, buffer, sizeof(float) * this->m_num_channels);
342     }
343   }
344
345   inline void readNoCheck(float *result,
346                           int x,
347                           int y,
348                           MemoryBufferExtend extend_x = MemoryBufferExtend::Clip,
349                           MemoryBufferExtend extend_y = MemoryBufferExtend::Clip)
350   {
351     int u = x;
352     int v = y;
353
354     this->wrap_pixel(u, v, extend_x, extend_y);
355     const int offset = get_coords_offset(u, v);
356
357     BLI_assert(offset >= 0);
358     BLI_assert(offset < this->buffer_len() * this->m_num_channels);
359     BLI_assert(!(extend_x == MemoryBufferExtend::Clip && (u < m_rect.xmin || u >= m_rect.xmax)) &&
360                !(extend_y == MemoryBufferExtend::Clip && (v < m_rect.ymin || v >= m_rect.ymax)));
361     float *buffer = &this->m_buffer[offset];
362     memcpy(result, buffer, sizeof(float) * this->m_num_channels);
363   }
364
365   void writePixel(int x, int y, const float color[4]);
366   void addPixel(int x, int y, const float color[4]);
367   inline void readBilinear(float *result,
368                            float x,
369                            float y,
370                            MemoryBufferExtend extend_x = MemoryBufferExtend::Clip,
371                            MemoryBufferExtend extend_y = MemoryBufferExtend::Clip)
372   {
373     float u = x;
374     float v = y;
375     this->wrap_pixel(u, v, extend_x, extend_y);
376     if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= getWidth())) ||
377         (extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= getHeight()))) {
378       copy_vn_fl(result, this->m_num_channels, 0.0f);
379       return;
380     }
381     if (m_is_a_single_elem) {
382       memcpy(result, m_buffer, sizeof(float) * this->m_num_channels);
383     }
384     else {
385       BLI_bilinear_interpolation_wrap_fl(this->m_buffer,
386                                          result,
387                                          getWidth(),
388                                          getHeight(),
389                                          this->m_num_channels,
390                                          u,
391                                          v,
392                                          extend_x == MemoryBufferExtend::Repeat,
393                                          extend_y == MemoryBufferExtend::Repeat);
394     }
395   }
396
397   void readEWA(float *result, const float uv[2], const float derivatives[2][2]);
398
399   /**
400    * \brief is this MemoryBuffer a temporarily buffer (based on an area, not on a chunk)
401    */
402   inline bool isTemporarily() const
403   {
404     return this->m_state == MemoryBufferState::Temporary;
405   }
406
407   /**
408    * \brief add the content from otherBuffer to this MemoryBuffer
409    * \param otherBuffer: source buffer
410    *
411    * \note take care when running this on a new buffer since it wont fill in
412    *       uninitialized values in areas where the buffers don't overlap.
413    */
414   void fill_from(const MemoryBuffer &src);
415
416   /**
417    * \brief get the rect of this MemoryBuffer
418    */
419   const rcti &get_rect() const
420   {
421     return this->m_rect;
422   }
423
424   /**
425    * \brief get the width of this MemoryBuffer
426    */
427   const int getWidth() const
428   {
429     return BLI_rcti_size_x(&m_rect);
430   }
431
432   /**
433    * \brief get the height of this MemoryBuffer
434    */
435   const int getHeight() const
436   {
437     return BLI_rcti_size_y(&m_rect);
438   }
439
440   /**
441    * \brief clear the buffer. Make all pixels black transparent.
442    */
443   void clear();
444
445   float get_max_value() const;
446   float get_max_value(const rcti &rect) const;
447
448  private:
449   void set_strides();
450   const int buffer_len() const
451   {
452     return get_memory_width() * get_memory_height();
453   }
454
455 #ifdef WITH_CXX_GUARDEDALLOC
456   MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryBuffer")
457 #endif
458 };
459
460 }  // namespace blender::compositor