/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright 2011, Blender Foundation. */ #pragma once #include "COM_ExecutionGroup.h" #include "COM_MemoryProxy.h" #include "BLI_math.h" #include "BLI_rect.h" namespace blender::compositor { /** * \brief state of a memory buffer * \ingroup Memory */ enum class MemoryBufferState { /** \brief memory has been allocated on creator device and CPU machine, * but kernel has not been executed */ Default = 0, /** \brief chunk is consolidated from other chunks. special state.*/ Temporary = 6, }; enum class MemoryBufferExtend { Clip, Extend, Repeat, }; class MemoryProxy; /** * \brief a MemoryBuffer contains access to the data of a chunk */ class MemoryBuffer { private: /** * \brief proxy of the memory (same for all chunks in the same buffer) */ MemoryProxy *m_memoryProxy; /** * \brief the type of buffer DataType::Value, DataType::Vector, DataType::Color */ DataType m_datatype; /** * \brief region of this buffer inside relative to the MemoryProxy */ rcti m_rect; /** * \brief state of the buffer */ MemoryBufferState m_state; /** * \brief the actual float buffer/data */ float *m_buffer; /** * \brief the number of channels of a single value in the buffer. * For value buffers this is 1, vector 3 and color 4 */ uint8_t m_num_channels; public: /** * \brief construct new temporarily MemoryBuffer for an area */ MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state); /** * \brief construct new temporarily MemoryBuffer for an area */ MemoryBuffer(DataType datatype, const rcti &rect); /** * Copy constructor */ MemoryBuffer(const MemoryBuffer &src); /** * \brief destructor */ ~MemoryBuffer(); uint8_t get_num_channels() { return this->m_num_channels; } /** * \brief get the data of this MemoryBuffer * \note buffer should already be available in memory */ float *getBuffer() { return this->m_buffer; } inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) { const int w = getWidth(); const int h = getHeight(); x = x - m_rect.xmin; y = y - m_rect.ymin; switch (extend_x) { case MemoryBufferExtend::Clip: break; case MemoryBufferExtend::Extend: if (x < 0) { x = 0; } if (x >= w) { x = w; } break; case MemoryBufferExtend::Repeat: x = (x >= 0.0f ? (x % w) : (x % w) + w); break; } switch (extend_y) { case MemoryBufferExtend::Clip: break; case MemoryBufferExtend::Extend: if (y < 0) { y = 0; } if (y >= h) { y = h; } break; case MemoryBufferExtend::Repeat: y = (y >= 0.0f ? (y % h) : (y % h) + h); break; } } inline void wrap_pixel(float &x, float &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) { const float w = (float)getWidth(); const float h = (float)getHeight(); x = x - m_rect.xmin; y = y - m_rect.ymin; switch (extend_x) { case MemoryBufferExtend::Clip: break; case MemoryBufferExtend::Extend: if (x < 0) { x = 0.0f; } if (x >= w) { x = w; } break; case MemoryBufferExtend::Repeat: x = fmodf(x, w); break; } switch (extend_y) { case MemoryBufferExtend::Clip: break; case MemoryBufferExtend::Extend: if (y < 0) { y = 0.0f; } if (y >= h) { y = h; } break; case MemoryBufferExtend::Repeat: y = fmodf(y, h); break; } } inline void read(float *result, int x, int y, MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) { bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < m_rect.xmin || x >= m_rect.xmax)); bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < m_rect.ymin || y >= m_rect.ymax)); if (clip_x || clip_y) { /* clip result outside rect is zero */ memset(result, 0, this->m_num_channels * sizeof(float)); } else { int u = x; int v = y; this->wrap_pixel(u, v, extend_x, extend_y); const int offset = (getWidth() * y + x) * this->m_num_channels; float *buffer = &this->m_buffer[offset]; memcpy(result, buffer, sizeof(float) * this->m_num_channels); } } inline void readNoCheck(float *result, int x, int y, MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) { int u = x; int v = y; this->wrap_pixel(u, v, extend_x, extend_y); const int offset = (getWidth() * v + u) * this->m_num_channels; BLI_assert(offset >= 0); BLI_assert(offset < this->buffer_len() * this->m_num_channels); BLI_assert(!(extend_x == MemoryBufferExtend::Clip && (u < m_rect.xmin || u >= m_rect.xmax)) && !(extend_y == MemoryBufferExtend::Clip && (v < m_rect.ymin || v >= m_rect.ymax))); float *buffer = &this->m_buffer[offset]; memcpy(result, buffer, sizeof(float) * this->m_num_channels); } void writePixel(int x, int y, const float color[4]); void addPixel(int x, int y, const float color[4]); inline void readBilinear(float *result, float x, float y, MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) { float u = x; float v = y; this->wrap_pixel(u, v, extend_x, extend_y); if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= getWidth())) || (extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= getHeight()))) { copy_vn_fl(result, this->m_num_channels, 0.0f); return; } BLI_bilinear_interpolation_wrap_fl(this->m_buffer, result, getWidth(), getHeight(), this->m_num_channels, u, v, extend_x == MemoryBufferExtend::Repeat, extend_y == MemoryBufferExtend::Repeat); } void readEWA(float *result, const float uv[2], const float derivatives[2][2]); /** * \brief is this MemoryBuffer a temporarily buffer (based on an area, not on a chunk) */ inline bool isTemporarily() const { return this->m_state == MemoryBufferState::Temporary; } /** * \brief add the content from otherBuffer to this MemoryBuffer * \param otherBuffer: source buffer * * \note take care when running this on a new buffer since it wont fill in * uninitialized values in areas where the buffers don't overlap. */ void fill_from(const MemoryBuffer &src); /** * \brief get the rect of this MemoryBuffer */ const rcti &get_rect() const { return this->m_rect; } /** * \brief get the width of this MemoryBuffer */ const int getWidth() const { return BLI_rcti_size_x(&m_rect); } /** * \brief get the height of this MemoryBuffer */ const int getHeight() const { return BLI_rcti_size_y(&m_rect); } /** * \brief clear the buffer. Make all pixels black transparent. */ void clear(); float get_max_value() const; float get_max_value(const rcti &rect) const; private: const int buffer_len() const { return getWidth() * getHeight(); } #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:MemoryBuffer") #endif }; } // namespace blender::compositor