Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/imbuf/intern')
-rw-r--r--source/blender/imbuf/intern/rasterizer_blending.hh32
-rw-r--r--source/blender/imbuf/intern/rasterizer_clamping.hh82
-rw-r--r--source/blender/imbuf/intern/rasterizer_stats.hh89
-rw-r--r--source/blender/imbuf/intern/rasterizer_target.hh74
-rw-r--r--source/blender/imbuf/intern/rasterizer_test.cc429
5 files changed, 706 insertions, 0 deletions
diff --git a/source/blender/imbuf/intern/rasterizer_blending.hh b/source/blender/imbuf/intern/rasterizer_blending.hh
new file mode 100644
index 00000000000..14dbaadfc95
--- /dev/null
+++ b/source/blender/imbuf/intern/rasterizer_blending.hh
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#pragma once
+
+#include "BLI_math.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_sys_types.h"
+
+namespace blender::imbuf::rasterizer {
+
+/** How to integrate the result of a fragment shader into its drawing target. */
+template<typename Source, typename Destination> class AbstractBlendMode {
+ public:
+ using SourceType = Source;
+ using DestinationType = Destination;
+
+ virtual void blend(Destination *dest, const Source &source) const = 0;
+};
+
+/**
+ * Copy the result of the fragment shader into float[4] without any modifications.
+ */
+class CopyBlendMode : public AbstractBlendMode<float4, float> {
+ public:
+ void blend(float *dest, const float4 &source) const override
+ {
+ copy_v4_v4(dest, source);
+ }
+};
+
+} // namespace blender::imbuf::rasterizer
diff --git a/source/blender/imbuf/intern/rasterizer_clamping.hh b/source/blender/imbuf/intern/rasterizer_clamping.hh
new file mode 100644
index 00000000000..30aa9daec36
--- /dev/null
+++ b/source/blender/imbuf/intern/rasterizer_clamping.hh
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#pragma once
+
+/** \file
+ * \ingroup imbuf
+ *
+ * Pixel clamping determines how the edges of geometry is clamped to pixels.
+ */
+
+#include "BLI_sys_types.h"
+
+namespace blender::imbuf::rasterizer {
+
+class AbstractPixelClampingMethod {
+ public:
+ virtual float distance_to_scanline_anchor(float y) const = 0;
+ virtual float distance_to_column_anchor(float y) const = 0;
+ virtual int scanline_for(float y) const = 0;
+ virtual int column_for(float x) const = 0;
+};
+
+class CenterPixelClampingMethod : public AbstractPixelClampingMethod {
+ public:
+ float distance_to_scanline_anchor(float y) const override
+ {
+ return distance_to_anchor(y);
+ }
+ float distance_to_column_anchor(float x) const override
+ {
+ return distance_to_anchor(x);
+ }
+
+ int scanline_for(float y) const override
+ {
+ return this->round(y);
+ }
+
+ int column_for(float x) const override
+ {
+ return this->round(x);
+ }
+
+ private:
+ float distance_to_anchor(float value) const
+ {
+ float fract = to_fract(value);
+ float result;
+ if (fract <= 0.5f) {
+ result = 0.5f - fract;
+ }
+ else {
+ result = 1.5f - fract;
+ }
+ BLI_assert(result >= 0.0f);
+ BLI_assert(result < 1.0f);
+ return result;
+ }
+
+ int round(float value) const
+ {
+ /* Cannot use std::round as it rounds away from 0. */
+ float fract = to_fract(value);
+ int result;
+
+ if (fract > 0.5f) {
+ result = ceilf(value);
+ }
+ else {
+ result = floorf(value);
+ }
+ return result;
+ }
+
+ float to_fract(float value) const
+ {
+ return value - floor(value);
+ }
+};
+
+} // namespace blender::imbuf::rasterizer
diff --git a/source/blender/imbuf/intern/rasterizer_stats.hh b/source/blender/imbuf/intern/rasterizer_stats.hh
new file mode 100644
index 00000000000..4a8ad4bff73
--- /dev/null
+++ b/source/blender/imbuf/intern/rasterizer_stats.hh
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#pragma once
+
+#include "BLI_sys_types.h"
+
+namespace blender::imbuf::rasterizer {
+
+class AbstractStats {
+ public:
+ virtual void increase_triangles() = 0;
+ virtual void increase_discarded_triangles() = 0;
+ virtual void increase_flushes() = 0;
+ virtual void increase_rasterlines() = 0;
+ virtual void increase_clamped_rasterlines() = 0;
+ virtual void increase_discarded_rasterlines() = 0;
+ virtual void increase_drawn_fragments(uint64_t fragments_drawn) = 0;
+};
+
+class Stats : public AbstractStats {
+ public:
+ int64_t triangles = 0;
+ int64_t discarded_triangles = 0;
+ int64_t flushes = 0;
+ int64_t rasterlines = 0;
+ int64_t clamped_rasterlines = 0;
+ int64_t discarded_rasterlines = 0;
+ int64_t drawn_fragments = 0;
+
+ void increase_triangles() override
+ {
+ triangles += 1;
+ }
+
+ void increase_discarded_triangles() override
+ {
+ discarded_triangles += 1;
+ }
+
+ void increase_flushes() override
+ {
+ flushes += 1;
+ }
+
+ void increase_rasterlines() override
+ {
+ rasterlines += 1;
+ }
+
+ void increase_clamped_rasterlines() override
+ {
+ clamped_rasterlines += 1;
+ }
+ void increase_discarded_rasterlines() override
+ {
+ discarded_rasterlines += 1;
+ }
+ void increase_drawn_fragments(uint64_t fragments_drawn) override
+ {
+ drawn_fragments += fragments_drawn;
+ }
+
+ void reset()
+ {
+ triangles = 0;
+ discarded_triangles = 0;
+ flushes = 0;
+ rasterlines = 0;
+ clamped_rasterlines = 0;
+ discarded_rasterlines = 0;
+ drawn_fragments = 0;
+ }
+};
+
+class NullStats : public AbstractStats {
+ public:
+ void increase_triangles() override{};
+ void increase_discarded_triangles() override{};
+ void increase_flushes() override{};
+ void increase_rasterlines() override{};
+ void increase_clamped_rasterlines() override{};
+ void increase_discarded_rasterlines() override{};
+ void increase_drawn_fragments(uint64_t UNUSED(fragments_drawn)) override
+ {
+ }
+};
+
+} // namespace blender::imbuf::rasterizer
diff --git a/source/blender/imbuf/intern/rasterizer_target.hh b/source/blender/imbuf/intern/rasterizer_target.hh
new file mode 100644
index 00000000000..25830b4006a
--- /dev/null
+++ b/source/blender/imbuf/intern/rasterizer_target.hh
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#pragma once
+
+/** \file
+ * \ingroup imbuf
+ *
+ * Rasterizer drawing target.
+ */
+
+#include "BLI_sys_types.h"
+
+namespace blender::imbuf::rasterizer {
+
+/**
+ * An abstract implementation of a drawing target. Will make it possible to switch to other render
+ * targets then only ImBuf types.
+ */
+template<typename Inner> class AbstractDrawingTarget {
+ public:
+ using InnerType = Inner;
+ virtual uint64_t get_width() const = 0;
+ virtual uint64_t get_height() const = 0;
+ virtual float *get_pixel_ptr(uint64_t x, uint64_t y) = 0;
+ virtual int64_t get_pixel_stride() const = 0;
+ virtual bool has_active_target() const = 0;
+ virtual void activate(Inner *instance) = 0;
+ virtual void deactivate() = 0;
+};
+
+class ImageBufferDrawingTarget : public AbstractDrawingTarget<ImBuf> {
+ private:
+ ImBuf *image_buffer_ = nullptr;
+
+ public:
+ bool has_active_target() const override
+ {
+ return image_buffer_ != nullptr;
+ }
+
+ void activate(ImBuf *image_buffer) override
+ {
+ image_buffer_ = image_buffer;
+ }
+
+ void deactivate() override
+ {
+ image_buffer_ = nullptr;
+ }
+
+ uint64_t get_width() const override
+ {
+ return image_buffer_->x;
+ };
+
+ uint64_t get_height() const override
+ {
+ return image_buffer_->y;
+ }
+
+ float *get_pixel_ptr(uint64_t x, uint64_t y) override
+ {
+ BLI_assert(has_active_target());
+ uint64_t pixel_index = y * image_buffer_->x + x;
+ return &image_buffer_->rect_float[pixel_index * 4];
+ }
+ int64_t get_pixel_stride() const override
+ {
+ return 4;
+ }
+};
+
+} // namespace blender::imbuf::rasterizer
diff --git a/source/blender/imbuf/intern/rasterizer_test.cc b/source/blender/imbuf/intern/rasterizer_test.cc
new file mode 100644
index 00000000000..ccc61d55ed9
--- /dev/null
+++ b/source/blender/imbuf/intern/rasterizer_test.cc
@@ -0,0 +1,429 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include "testing/testing.h"
+
+#include "BLI_float4x4.hh"
+#include "BLI_path_util.h"
+
+#include "IMB_rasterizer.hh"
+
+namespace blender::imbuf::rasterizer::tests {
+
+const uint32_t IMBUF_SIZE = 256;
+
+struct VertexInput {
+ float2 uv;
+ float value;
+
+ VertexInput(float2 uv, float value) : uv(uv), value(value)
+ {
+ }
+};
+
+class VertexShader : public AbstractVertexShader<VertexInput, float4> {
+ public:
+ float2 image_size;
+ float4x4 vp_mat;
+ void vertex(const VertexInputType &input, VertexOutputType *r_output) override
+ {
+ float2 coord = float2(vp_mat * float3(input.uv[0], input.uv[1], 0.0));
+ r_output->coord = coord * image_size;
+ r_output->data = float4(input.value, input.value, input.value, 1.0);
+ }
+};
+
+class FragmentShader : public AbstractFragmentShader<float4, float4> {
+ public:
+ void fragment(const FragmentInputType &input, FragmentOutputType *r_output) override
+ {
+ *r_output = input;
+ }
+};
+
+using RasterizerType = Rasterizer<VertexShader,
+ FragmentShader,
+ CopyBlendMode,
+ ImageBufferDrawingTarget,
+ DefaultRasterlinesBufferSize,
+ Stats>;
+
+/* Draw 2 triangles that fills the entire image buffer and see if each pixel is touched. */
+TEST(imbuf_rasterizer, draw_triangle_edge_alignment_quality)
+{
+ ImBuf image_buffer;
+ IMB_initImBuf(&image_buffer, IMBUF_SIZE, IMBUF_SIZE, 32, IB_rectfloat);
+
+ RasterizerType rasterizer;
+ rasterizer.activate_drawing_target(&image_buffer);
+
+ VertexShader &vertex_shader = rasterizer.vertex_shader();
+ vertex_shader.image_size = float2(image_buffer.x, image_buffer.y);
+
+ float clear_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+
+ float3 location(0.5, 0.5, 0.0);
+ float3 rotation(0.0, 0.0, 0.0);
+ float3 scale(1.0, 1.0, 1.0);
+
+ for (int i = 0; i < 1000; i++) {
+ rasterizer.stats.reset();
+
+ IMB_rectfill(&image_buffer, clear_color);
+ rotation[2] = (i / 1000.0) * M_PI * 2;
+
+ vertex_shader.vp_mat = float4x4::from_loc_eul_scale(location, rotation, scale);
+ rasterizer.draw_triangle(VertexInput(float2(-1.0, -1.0), 0.2),
+ VertexInput(float2(-1.0, 1.0), 0.5),
+ VertexInput(float2(1.0, -1.0), 1.0));
+ rasterizer.draw_triangle(VertexInput(float2(1.0, 1.0), 0.2),
+ VertexInput(float2(-1.0, 1.0), 0.5),
+ VertexInput(float2(1.0, -1.0), 1.0));
+ rasterizer.flush();
+
+ /* Check if each pixel has been drawn exactly once. */
+ EXPECT_EQ(rasterizer.stats.drawn_fragments, IMBUF_SIZE * IMBUF_SIZE) << i;
+
+#ifdef DEBUG_SAVE
+ char file_name[FILE_MAX];
+ BLI_path_sequence_encode(file_name, "/tmp/test_", ".png", 4, i);
+ IMB_saveiff(&image_buffer, file_name, IB_rectfloat);
+ imb_freerectImBuf(&image_buffer);
+#endif
+ }
+
+ imb_freerectImbuf_all(&image_buffer);
+}
+
+/**
+ * This test case renders 3 images that should have the same coverage. But using a different edge.
+ *
+ * The results should be identical.
+ */
+TEST(imbuf_rasterizer, edge_pixel_clamping)
+{
+ float clear_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+
+ ImBuf image_buffer_a;
+ ImBuf image_buffer_b;
+ ImBuf image_buffer_c;
+ int fragments_drawn_a;
+ int fragments_drawn_b;
+ int fragments_drawn_c;
+
+ RasterizerType rasterizer;
+
+ {
+ IMB_initImBuf(&image_buffer_a, IMBUF_SIZE, IMBUF_SIZE, 32, IB_rectfloat);
+
+ rasterizer.stats.reset();
+ rasterizer.activate_drawing_target(&image_buffer_a);
+ VertexShader &vertex_shader = rasterizer.vertex_shader();
+ vertex_shader.image_size = float2(image_buffer_a.x, image_buffer_a.y);
+ vertex_shader.vp_mat = float4x4::identity();
+ IMB_rectfill(&image_buffer_a, clear_color);
+ rasterizer.draw_triangle(VertexInput(float2(0.2, -0.2), 1.0),
+ VertexInput(float2(1.2, 1.2), 1.0),
+ VertexInput(float2(1.5, -0.3), 1.0));
+ rasterizer.flush();
+ fragments_drawn_a = rasterizer.stats.drawn_fragments;
+ }
+ {
+ IMB_initImBuf(&image_buffer_b, IMBUF_SIZE, IMBUF_SIZE, 32, IB_rectfloat);
+
+ rasterizer.stats.reset();
+ rasterizer.activate_drawing_target(&image_buffer_b);
+ VertexShader &vertex_shader = rasterizer.vertex_shader();
+ vertex_shader.image_size = float2(image_buffer_b.x, image_buffer_b.y);
+ vertex_shader.vp_mat = float4x4::identity();
+ IMB_rectfill(&image_buffer_b, clear_color);
+ rasterizer.draw_triangle(VertexInput(float2(0.2, -0.2), 1.0),
+ VertexInput(float2(1.2, 1.2), 1.0),
+ VertexInput(float2(1.5, -0.3), 1.0));
+ rasterizer.flush();
+ fragments_drawn_b = rasterizer.stats.drawn_fragments;
+ }
+
+ {
+ IMB_initImBuf(&image_buffer_c, IMBUF_SIZE, IMBUF_SIZE, 32, IB_rectfloat);
+
+ rasterizer.stats.reset();
+ rasterizer.activate_drawing_target(&image_buffer_c);
+ VertexShader &vertex_shader = rasterizer.vertex_shader();
+ vertex_shader.image_size = float2(image_buffer_c.x, image_buffer_c.y);
+ vertex_shader.vp_mat = float4x4::identity();
+ IMB_rectfill(&image_buffer_c, clear_color);
+ rasterizer.draw_triangle(VertexInput(float2(0.2, -0.2), 1.0),
+ VertexInput(float2(1.2, 1.2), 1.0),
+ VertexInput(float2(10.0, 1.3), 1.0));
+ rasterizer.flush();
+ fragments_drawn_c = rasterizer.stats.drawn_fragments;
+ }
+
+ EXPECT_EQ(fragments_drawn_a, fragments_drawn_b);
+ EXPECT_EQ(memcmp(image_buffer_a.rect_float,
+ image_buffer_b.rect_float,
+ sizeof(float) * 4 * IMBUF_SIZE * IMBUF_SIZE),
+ 0);
+ EXPECT_EQ(fragments_drawn_a, fragments_drawn_c);
+ EXPECT_EQ(memcmp(image_buffer_a.rect_float,
+ image_buffer_c.rect_float,
+ sizeof(float) * 4 * IMBUF_SIZE * IMBUF_SIZE),
+ 0);
+ EXPECT_EQ(fragments_drawn_b, fragments_drawn_c);
+ EXPECT_EQ(memcmp(image_buffer_b.rect_float,
+ image_buffer_c.rect_float,
+ sizeof(float) * 4 * IMBUF_SIZE * IMBUF_SIZE),
+ 0);
+
+ imb_freerectImbuf_all(&image_buffer_a);
+ imb_freerectImbuf_all(&image_buffer_b);
+ imb_freerectImbuf_all(&image_buffer_c);
+}
+
+/** Use one rasterizer and switch between multiple drawing targets. */
+TEST(imbuf_rasterizer, switch_drawing_target)
+{
+ float clear_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+
+ ImBuf image_buffer_a;
+ ImBuf image_buffer_b;
+ ImBuf image_buffer_c;
+
+ RasterizerType rasterizer;
+
+ IMB_initImBuf(&image_buffer_a, IMBUF_SIZE, IMBUF_SIZE, 32, IB_rectfloat);
+ IMB_rectfill(&image_buffer_a, clear_color);
+
+ VertexShader &vertex_shader = rasterizer.vertex_shader();
+ vertex_shader.image_size = float2(image_buffer_a.x, image_buffer_a.y);
+ vertex_shader.vp_mat = float4x4::identity();
+
+ rasterizer.activate_drawing_target(&image_buffer_a);
+ rasterizer.draw_triangle(VertexInput(float2(0.2, -0.2), 1.0),
+ VertexInput(float2(1.2, 1.2), 1.0),
+ VertexInput(float2(1.5, -0.3), 1.0));
+
+ IMB_initImBuf(&image_buffer_b, IMBUF_SIZE, IMBUF_SIZE, 32, IB_rectfloat);
+ IMB_rectfill(&image_buffer_b, clear_color);
+ rasterizer.activate_drawing_target(&image_buffer_b);
+ rasterizer.draw_triangle(VertexInput(float2(0.2, -0.2), 1.0),
+ VertexInput(float2(1.2, 1.2), 1.0),
+ VertexInput(float2(1.5, -0.3), 1.0));
+
+ IMB_initImBuf(&image_buffer_c, IMBUF_SIZE, IMBUF_SIZE, 32, IB_rectfloat);
+ IMB_rectfill(&image_buffer_c, clear_color);
+ rasterizer.activate_drawing_target(&image_buffer_c);
+ rasterizer.draw_triangle(VertexInput(float2(0.2, -0.2), 1.0),
+ VertexInput(float2(1.2, 1.2), 1.0),
+ VertexInput(float2(10.0, 1.3), 1.0));
+ rasterizer.flush();
+
+ EXPECT_EQ(memcmp(image_buffer_a.rect_float,
+ image_buffer_b.rect_float,
+ sizeof(float) * 4 * IMBUF_SIZE * IMBUF_SIZE),
+ 0);
+ EXPECT_EQ(memcmp(image_buffer_a.rect_float,
+ image_buffer_c.rect_float,
+ sizeof(float) * 4 * IMBUF_SIZE * IMBUF_SIZE),
+ 0);
+ EXPECT_EQ(memcmp(image_buffer_b.rect_float,
+ image_buffer_c.rect_float,
+ sizeof(float) * 4 * IMBUF_SIZE * IMBUF_SIZE),
+ 0);
+
+ imb_freerectImbuf_all(&image_buffer_a);
+ imb_freerectImbuf_all(&image_buffer_b);
+ imb_freerectImbuf_all(&image_buffer_c);
+}
+
+TEST(imbuf_rasterizer, center_pixel_clamper_scanline_for)
+{
+ CenterPixelClampingMethod clamper;
+
+ EXPECT_EQ(clamper.scanline_for(-2.0f), -2);
+ EXPECT_EQ(clamper.scanline_for(-1.9f), -2);
+ EXPECT_EQ(clamper.scanline_for(-1.8f), -2);
+ EXPECT_EQ(clamper.scanline_for(-1.7f), -2);
+ EXPECT_EQ(clamper.scanline_for(-1.6f), -2);
+ EXPECT_EQ(clamper.scanline_for(-1.5f), -2);
+ EXPECT_EQ(clamper.scanline_for(-1.4f), -1);
+ EXPECT_EQ(clamper.scanline_for(-1.3f), -1);
+ EXPECT_EQ(clamper.scanline_for(-1.2f), -1);
+ EXPECT_EQ(clamper.scanline_for(-1.1f), -1);
+ EXPECT_EQ(clamper.scanline_for(-1.0f), -1);
+ EXPECT_EQ(clamper.scanline_for(-0.9f), -1);
+ EXPECT_EQ(clamper.scanline_for(-0.8f), -1);
+ EXPECT_EQ(clamper.scanline_for(-0.7f), -1);
+ EXPECT_EQ(clamper.scanline_for(-0.6f), -1);
+ EXPECT_EQ(clamper.scanline_for(-0.5f), -1);
+ EXPECT_EQ(clamper.scanline_for(-0.4f), 0);
+ EXPECT_EQ(clamper.scanline_for(-0.3f), 0);
+ EXPECT_EQ(clamper.scanline_for(-0.2f), 0);
+ EXPECT_EQ(clamper.scanline_for(-0.1f), 0);
+ EXPECT_EQ(clamper.scanline_for(0.0f), 0);
+ EXPECT_EQ(clamper.scanline_for(0.1f), 0);
+ EXPECT_EQ(clamper.scanline_for(0.2f), 0);
+ EXPECT_EQ(clamper.scanline_for(0.3f), 0);
+ EXPECT_EQ(clamper.scanline_for(0.4f), 0);
+ EXPECT_EQ(clamper.scanline_for(0.5f), 0);
+ EXPECT_EQ(clamper.scanline_for(0.6f), 1);
+ EXPECT_EQ(clamper.scanline_for(0.7f), 1);
+ EXPECT_EQ(clamper.scanline_for(0.8f), 1);
+ EXPECT_EQ(clamper.scanline_for(0.9f), 1);
+ EXPECT_EQ(clamper.scanline_for(1.0f), 1);
+ EXPECT_EQ(clamper.scanline_for(1.0f), 1);
+ EXPECT_EQ(clamper.scanline_for(1.1f), 1);
+ EXPECT_EQ(clamper.scanline_for(1.2f), 1);
+ EXPECT_EQ(clamper.scanline_for(1.3f), 1);
+ EXPECT_EQ(clamper.scanline_for(1.4f), 1);
+ EXPECT_EQ(clamper.scanline_for(1.5f), 1);
+ EXPECT_EQ(clamper.scanline_for(1.6f), 2);
+ EXPECT_EQ(clamper.scanline_for(1.7f), 2);
+ EXPECT_EQ(clamper.scanline_for(1.8f), 2);
+ EXPECT_EQ(clamper.scanline_for(1.9f), 2);
+ EXPECT_EQ(clamper.scanline_for(2.0f), 2);
+}
+
+TEST(imbuf_rasterizer, center_pixel_clamper_column_for)
+{
+ CenterPixelClampingMethod clamper;
+
+ EXPECT_EQ(clamper.column_for(-2.0f), -2);
+ EXPECT_EQ(clamper.column_for(-1.9f), -2);
+ EXPECT_EQ(clamper.column_for(-1.8f), -2);
+ EXPECT_EQ(clamper.column_for(-1.7f), -2);
+ EXPECT_EQ(clamper.column_for(-1.6f), -2);
+ EXPECT_EQ(clamper.column_for(-1.5f), -2);
+ EXPECT_EQ(clamper.column_for(-1.4f), -1);
+ EXPECT_EQ(clamper.column_for(-1.3f), -1);
+ EXPECT_EQ(clamper.column_for(-1.2f), -1);
+ EXPECT_EQ(clamper.column_for(-1.1f), -1);
+ EXPECT_EQ(clamper.column_for(-1.0f), -1);
+ EXPECT_EQ(clamper.column_for(-0.9f), -1);
+ EXPECT_EQ(clamper.column_for(-0.8f), -1);
+ EXPECT_EQ(clamper.column_for(-0.7f), -1);
+ EXPECT_EQ(clamper.column_for(-0.6f), -1);
+ EXPECT_EQ(clamper.column_for(-0.5f), -1);
+ EXPECT_EQ(clamper.column_for(-0.4f), 0);
+ EXPECT_EQ(clamper.column_for(-0.3f), 0);
+ EXPECT_EQ(clamper.column_for(-0.2f), 0);
+ EXPECT_EQ(clamper.column_for(-0.1f), 0);
+ EXPECT_EQ(clamper.column_for(0.0f), 0);
+ EXPECT_EQ(clamper.column_for(0.1f), 0);
+ EXPECT_EQ(clamper.column_for(0.2f), 0);
+ EXPECT_EQ(clamper.column_for(0.3f), 0);
+ EXPECT_EQ(clamper.column_for(0.4f), 0);
+ EXPECT_EQ(clamper.column_for(0.5f), 0);
+ EXPECT_EQ(clamper.column_for(0.6f), 1);
+ EXPECT_EQ(clamper.column_for(0.7f), 1);
+ EXPECT_EQ(clamper.column_for(0.8f), 1);
+ EXPECT_EQ(clamper.column_for(0.9f), 1);
+ EXPECT_EQ(clamper.column_for(1.0f), 1);
+ EXPECT_EQ(clamper.column_for(1.0f), 1);
+ EXPECT_EQ(clamper.column_for(1.1f), 1);
+ EXPECT_EQ(clamper.column_for(1.2f), 1);
+ EXPECT_EQ(clamper.column_for(1.3f), 1);
+ EXPECT_EQ(clamper.column_for(1.4f), 1);
+ EXPECT_EQ(clamper.column_for(1.5f), 1);
+ EXPECT_EQ(clamper.column_for(1.6f), 2);
+ EXPECT_EQ(clamper.column_for(1.7f), 2);
+ EXPECT_EQ(clamper.column_for(1.8f), 2);
+ EXPECT_EQ(clamper.column_for(1.9f), 2);
+ EXPECT_EQ(clamper.column_for(2.0f), 2);
+}
+
+TEST(imbuf_rasterizer, center_pixel_clamper_distance_to_scanline_anchorpoint)
+{
+ CenterPixelClampingMethod clamper;
+
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-2.0f), 0.5f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.9f), 0.4f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.8f), 0.3f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.7f), 0.2f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.6f), 0.1f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.5f), 0.0f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.4f), 0.9f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.3f), 0.8f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.2f), 0.7f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.1f), 0.6f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-1.0f), 0.5f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-0.9f), 0.4f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-0.8f), 0.3f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-0.7f), 0.2f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-0.6f), 0.1f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-0.5f), 0.0f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-0.4f), 0.9f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-0.3f), 0.8f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-0.2f), 0.7f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(-0.1f), 0.6f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.0f), 0.5f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.1f), 0.4f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.2f), 0.3f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.3f), 0.2f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.4f), 0.1f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.5f), 0.0f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.6f), 0.9f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.7f), 0.8f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.8f), 0.7f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(0.9f), 0.6f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.0f), 0.5f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.1f), 0.4f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.2f), 0.3f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.3f), 0.2f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.4f), 0.1f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.5f), 0.0f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.6f), 0.9f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.7f), 0.8f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.8f), 0.7f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(1.9f), 0.6f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_scanline_anchor(2.0f), 0.5f);
+}
+
+TEST(imbuf_rasterizer, center_pixel_clamper_distance_to_column_anchorpoint)
+{
+ CenterPixelClampingMethod clamper;
+
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-2.0f), 0.5f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.9f), 0.4f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.8f), 0.3f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.7f), 0.2f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.6f), 0.1f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.5f), 0.0f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.4f), 0.9f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.3f), 0.8f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.2f), 0.7f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.1f), 0.6f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-1.0f), 0.5f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-0.9f), 0.4f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-0.8f), 0.3f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-0.7f), 0.2f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-0.6f), 0.1f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-0.5f), 0.0f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-0.4f), 0.9f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-0.3f), 0.8f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-0.2f), 0.7f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(-0.1f), 0.6f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.0f), 0.5f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.1f), 0.4f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.2f), 0.3f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.3f), 0.2f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.4f), 0.1f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.5f), 0.0f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.6f), 0.9f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.7f), 0.8f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.8f), 0.7f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(0.9f), 0.6f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.0f), 0.5f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.1f), 0.4f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.2f), 0.3f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.3f), 0.2f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.4f), 0.1f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.5f), 0.0f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.6f), 0.9f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.7f), 0.8f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.8f), 0.7f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(1.9f), 0.6f);
+ EXPECT_FLOAT_EQ(clamper.distance_to_column_anchor(2.0f), 0.5f);
+}
+
+} // namespace blender::imbuf::rasterizer::tests