[Bf-blender-cvs] [723fb163436] master: Images: 1, 2, 3 channel support for transform function.

Jeroen Bakker noreply at git.blender.org
Wed Dec 15 11:11:41 CET 2021


Commit: 723fb163436b2d5f1fbe835f84ba018307c30a0e
Author: Jeroen Bakker
Date:   Wed Dec 15 11:09:31 2021 +0100
Branches: master
https://developer.blender.org/rB723fb163436b2d5f1fbe835f84ba018307c30a0e

Images: 1,2,3 channel support for transform function.

Added support for 1, 2, 3 float channel source images. Destination
images must still be 4 channels.

===================================================================

M	source/blender/imbuf/IMB_imbuf.h
M	source/blender/imbuf/intern/transform.cc

===================================================================

diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index f71eef66a62..a0236b8ec4d 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -908,6 +908,25 @@ typedef enum eIMBTransformMode {
   IMB_TRANSFORM_MODE_WRAP_REPEAT = 2,
 } eIMBTransformMode;
 
+/**
+ * \brief Transform source image buffer onto destination image buffer using a transform matrix.
+ *
+ * \param src Image buffer to read from.
+ * \param dst Image buffer to write to. rect or rect_float must already be initialized.
+ * - dst buffer must be a 4 channel buffers.
+ * - Only one data type buffer will be used (rect_float has priority over rect)
+ * \param mode Cropping/Wrap repeat effect to apply during transformation.
+ * \param filter Interpolation to use during sampling.
+ * \param transform_matrix Transformation matrix to use.
+ * The given matrix should transform between dst texel space to src texel space.
+ * One unit is one texel.
+ * \param src_crop cropping region how to crop the source buffer. Should only be passed when mode
+ * is set to IMB_TRANSFORM_MODE_CROP_SRC. For any other mode this should be empty.
+ *
+ * During transformation no data/color conversion will happens.
+ * When transforming between float images the number of channels of the source buffer may be
+ * between 1 and 4. When source buffer has one channel the data will be read as a grey scale value.
+ */
 void IMB_transform(const struct ImBuf *src,
                    struct ImBuf *dst,
                    const eIMBTransformMode mode,
diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc
index 0cb363c3bce..dc28d85eeea 100644
--- a/source/blender/imbuf/intern/transform.cc
+++ b/source/blender/imbuf/intern/transform.cc
@@ -21,6 +21,7 @@
  * \ingroup imbuf
  */
 
+#include <array>
 #include <type_traits>
 
 #include "BLI_math.h"
@@ -32,13 +33,33 @@
 namespace blender::imbuf::transform {
 
 struct TransformUserData {
+  /** \brief Source image buffer to read from. */
   const ImBuf *src;
+  /** \brief Destination image buffer to write to. */
   ImBuf *dst;
+  /** \brief UV coordinates at the origin (0,0) in source image space. */
   float start_uv[2];
+
+  /**
+   * \brief delta UV coordinates along the source image buffer, when moving a single texel in the X
+   * axis of the dst image buffer.
+   */
   float add_x[2];
+
+  /**
+   * \brief delta UV coordinate along the source image buffer, when moving a single texel in the Y
+   * axes of the dst image buffer.
+   */
   float add_y[2];
+
+  /**
+   * \brief Cropping region in source image texel space.
+   */
   rctf src_crop;
 
+  /**
+   * \brief Initialize the start_uv, add_x and add_y fields based on the given transform matrix.
+   */
   void init(const float transform_matrix[4][4])
   {
     init_start_uv(transform_matrix);
@@ -85,24 +106,369 @@ struct TransformUserData {
   }
 };
 
-template<eIMBTransformMode Mode, InterpolationColorFunction ColorInterpolation, int ChannelLen = 4>
-class ScanlineProcessor {
+/**
+ * \brief Base class for source discarding.
+ *
+ * The class decides if a specific uv coordinate from the source buffer should be ignored.
+ * This is used to mix multiple images over a single output buffer. Discarded pixels will
+ * not change the output buffer.
+ */
+class BaseDiscard {
+ public:
+  virtual ~BaseDiscard() = default;
+
+  /**
+   * \brief Should the source pixel at the given uv coordinate be discarded.
+   */
+  virtual bool should_discard(const TransformUserData &user_data, const float uv[2]) = 0;
+};
+
+/**
+ * \brief Crop uv-coordinates that are outside the user data src_crop rect.
+ */
+class CropSource : public BaseDiscard {
+ public:
+  /**
+   * \brief Should the source pixel at the given uv coordinate be discarded.
+   *
+   * Uses user_data.src_crop to determine if the uv coordinate should be skipped.
+   */
+  bool should_discard(const TransformUserData &user_data, const float uv[2]) override
+  {
+    return uv[0] < user_data.src_crop.xmin || uv[0] >= user_data.src_crop.xmax ||
+           uv[1] < user_data.src_crop.ymin || uv[1] >= user_data.src_crop.ymax;
+  }
+};
+
+/**
+ * \brief Discard that does not discard anything.
+ */
+class NoDiscard : public BaseDiscard {
+ public:
+  /**
+   * \brief Should the source pixel at the given uv coordinate be discarded.
+   *
+   * Will never discard any pixels.
+   */
+  bool should_discard(const TransformUserData &UNUSED(user_data),
+                      const float UNUSED(uv[2])) override
+  {
+    return false;
+  }
+};
+
+/**
+ * \brief Pointer to a texel to write to in serial.
+ */
+template<
+    /**
+     * \brief Kind of buffer.
+     * Possible options: float, unsigned char.
+     */
+    typename StorageType = float,
+
+    /**
+     * \brief Number of channels of a single pixel.
+     */
+    int NumChannels = 4>
+class TexelPointer {
+ public:
+  static const int ChannelLen = NumChannels;
+
  private:
-  void pixel_from_buffer(const struct ImBuf *ibuf, unsigned char **outI, float **outF, int y) const
+  StorageType *pointer;
+
+ public:
+  void init_pixel_pointer(const ImBuf *image_buffer, int x, int y)
+  {
+    const size_t offset = (y * (size_t)image_buffer->x + x) * NumChannels;
+
+    if constexpr (std::is_same_v<StorageType, float>) {
+      pointer = image_buffer->rect_float + offset;
+    }
+    else if constexpr (std::is_same_v<StorageType, unsigned char>) {
+      pointer = const_cast<unsigned char *>(
+          static_cast<const unsigned char *>(static_cast<const void *>(image_buffer->rect)) +
+          offset);
+    }
+    else {
+      pointer = nullptr;
+    }
+  }
+
+  /**
+   * \brief Get pointer to the current texel to write to.
+   */
+  StorageType *get_pointer()
+  {
+    return pointer;
+  }
+
+  void increase_pixel_pointer()
+  {
+    pointer += NumChannels;
+  }
+};
+
+/**
+ * \brief Wrapping mode for the uv coordinates.
+ *
+ * Subclasses have the ability to change the UV coordinates when sampling the source buffer.
+ */
+class BaseUVWrapping {
+ public:
+  /**
+   * \brief modify the given u coordinate.
+   */
+  virtual float modify_u(const ImBuf *source_buffer, float u) = 0;
+
+  /**
+   * \brief modify the given v coordinate.
+   */
+  virtual float modify_v(const ImBuf *source_buffer, float v) = 0;
+};
+
+/**
+ * \brief UVWrapping method that does not modify the UV coordinates.
+ */
+class PassThroughUV : public BaseUVWrapping {
+ public:
+  float modify_u(const ImBuf *UNUSED(source_buffer), float u) override
+  {
+    return u;
+  }
+
+  float modify_v(const ImBuf *UNUSED(source_buffer), float v) override
+  {
+    return v;
+  }
+};
+
+/**
+ * \brief UVWrapping method that wrap repeats the UV coordinates.
+ */
+class WrapRepeatUV : public BaseUVWrapping {
+ public:
+  float modify_u(const ImBuf *source_buffer, float u) override
+
+  {
+    int x = (int)floor(u);
+    x = x % source_buffer->x;
+    if (x < 0) {
+      x += source_buffer->x;
+    }
+    return x;
+  }
+
+  float modify_v(const ImBuf *source_buffer, float v) override
+  {
+    int y = (int)floor(v);
+    y = y % source_buffer->y;
+    if (y < 0) {
+      y += source_buffer->y;
+    }
+    return y;
+  }
+};
 
+/**
+ * \brief Read a sample from an image buffer.
+ *
+ * A sampler can read from an image buffer.
+ *
+ */
+template<
+    /** \brief Interpolation mode to use when sampling. */
+    eIMBInterpolationFilterMode Filter,
+
+    /** \brief storage type of a single texel channel (unsigned char or float). */
+    typename StorageType,
+    /**
+     * \brief number of channels if the image to read.
+     *
+     * Must match the actual channels of the image buffer that is sampled.
+     */
+    int NumChannels,
+    /**
+     * \brief Wrapping method to perform
+     *
+     * Should be a subclass of BaseUVWrapper
+     */
+    typename UVWrapping>
+class Sampler {
+  UVWrapping uv_wrapper;
+
+ public:
+  using ChannelType = StorageType;
+  static const int ChannelLen = NumChannels;
+  using SampleType = std::array<StorageType, NumChannels>;
+
+  void sample(const ImBuf *source, const float u, const float v, SampleType &r_sample)
   {
-    const size_t offset = ((size_t)ibuf->x) * y * ChannelLen;
+    const float wrapped_u = uv_wrapper.modify_u(source, u);
+    const float wrapped_v = uv_wrapper.modify_v(source, v);
 
-    if (ibuf->rect) {
-      *outI = (unsigned char *)ibuf->rect + offset;
+    if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float> &&
+                  NumChannels == 4) {
+      bilinear_interpolation_color_fl(source, nullptr, r_sample.begin(), wrapped_u, wrapped_v);
+    }
+    else if constexpr (Filter == IMB_FILTER_NEAREST &&
+                       std::is_same_v<StorageType, unsigned char> && NumChannels == 4) {
+      nearest_interpolation_color_char(source, r_sample.begin(), nullptr, wrapped_u, wrapped_v);
+    }
+    else if constexpr (Filter == IMB_FILTER_BILINEAR &&
+                       std::is_same_v<StorageType, unsigned char> && NumChannels == 4) {
+      bilinear_interpolation_color_char(source, r_sample.begin(), nullptr, wrapped_u, wrapped_v);
+    }
+    else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, float>) {
+      if constexpr (std::is_same_v<UVWrapping, WrapRepeatUV>) {
+        BLI_bilinear_interpolation_wrap_fl(source->rect_float,
+                                           r_sample.begin(),
+                                           source->x,
+                                           source->y,
+                                           NumChannels,
+                                           u,
+                                           v,
+                                           true,
+                                           true);
+      }
+      else {
+        BLI_bilinear_interpolation_fl(source->rect_float,
+                                      r_sample.begin(),
+                                      source->x,
+                                      source->y,
+                                      NumChannels,
+                                      wrapped_u,
+                                      wrapped_v);
+      }
+    }
+    

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list