diff options
Diffstat (limited to 'source/blender/imbuf/intern')
-rw-r--r-- | source/blender/imbuf/intern/rasterizer_blending.hh | 32 | ||||
-rw-r--r-- | source/blender/imbuf/intern/rasterizer_clamping.hh | 82 | ||||
-rw-r--r-- | source/blender/imbuf/intern/rasterizer_stats.hh | 89 | ||||
-rw-r--r-- | source/blender/imbuf/intern/rasterizer_target.hh | 74 | ||||
-rw-r--r-- | source/blender/imbuf/intern/rasterizer_test.cc | 429 |
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 |