Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,302 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "testing/testing.h"
#include "BLI_math_vector_types.hh"
#include "IMB_imbuf.hh"
#include "BKE_gtest_base.hh"
namespace blender::imbuf::tests {
class ImBufScalingTest : public bke::BlenderGTestBase {};
static ImBuf *create_6x2_test_image()
{
ImBuf *img = IMB_allocImBuf(6, 2, ImBufFlags::ByteData);
uchar4 *col = reinterpret_cast<uchar4 *>(img->byte_data_for_write());
/* Source pixels are spelled out in 2x2 blocks below:
* nearest filter results in corner pixel from each block, bilinear
* is average of each block. */
col[0] = uchar4(0, 0, 0, 255);
col[1] = uchar4(255, 0, 0, 255);
col[6] = uchar4(255, 255, 0, 255);
col[7] = uchar4(255, 255, 255, 255);
col[2] = uchar4(133, 55, 31, 13);
col[3] = uchar4(133, 55, 31, 15);
col[8] = uchar4(133, 55, 31, 17);
col[9] = uchar4(133, 55, 31, 19);
col[4] = uchar4(50, 200, 0, 255);
col[5] = uchar4(55, 0, 32, 254);
col[10] = uchar4(56, 0, 64, 253);
col[11] = uchar4(57, 0, 96, 252);
return img;
}
static ImBuf *create_6x2_test_image_fl(int channels)
{
ImBuf *img = IMB_allocImBuf(6, 2, ImBufFlags::FloatData);
img->channels = channels;
float *col = img->float_data_for_write();
for (int y = 0; y < img->y; y++) {
for (int x = 0; x < img->x; x++) {
for (int ch = 0; ch < channels; ch++) {
*col = x * 1.25f + y * 0.5f + ch * 0.125f;
col++;
}
}
}
return img;
}
static ImBuf *scale_2x_smaller(bool nearest, bool threaded, int float_channels = 0)
{
ImBuf *img = float_channels > 0 ? create_6x2_test_image_fl(float_channels) :
create_6x2_test_image();
int ww = 3, hh = 1;
if (threaded) {
IMB_scale(img, ww, hh, IMBScaleFilter::Bilinear, true);
}
else if (nearest) {
IMB_scale(img, ww, hh, IMBScaleFilter::Nearest, false);
}
else {
IMB_scale(img, ww, hh, IMBScaleFilter::Box, false);
}
return img;
}
static ImBuf *scale_to_1x1(bool nearest, bool threaded, int float_channels = 0)
{
ImBuf *img = float_channels > 0 ? create_6x2_test_image_fl(float_channels) :
create_6x2_test_image();
int ww = 1, hh = 1;
if (threaded) {
IMB_scale(img, ww, hh, IMBScaleFilter::Bilinear, true);
}
else if (nearest) {
IMB_scale(img, ww, hh, IMBScaleFilter::Nearest, false);
}
else {
IMB_scale(img, ww, hh, IMBScaleFilter::Box, false);
}
return img;
}
static ImBuf *scale_fractional_larger(bool nearest, bool threaded, int float_channels = 0)
{
ImBuf *img = float_channels > 0 ? create_6x2_test_image_fl(float_channels) :
create_6x2_test_image();
int ww = 9, hh = 7;
if (threaded) {
IMB_scale(img, ww, hh, IMBScaleFilter::Bilinear, true);
}
else if (nearest) {
IMB_scale(img, ww, hh, IMBScaleFilter::Nearest, false);
}
else {
IMB_scale(img, ww, hh, IMBScaleFilter::Box, false);
}
return img;
}
TEST_F(ImBufScalingTest, nearest_2x_smaller)
{
ImBuf *res = scale_2x_smaller(true, false);
const uchar4 *got = reinterpret_cast<const uchar4 *>(res->byte_data());
EXPECT_EQ(uint4(got[0]), uint4(0, 0, 0, 255));
EXPECT_EQ(uint4(got[1]), uint4(133, 55, 31, 13));
EXPECT_EQ(uint4(got[2]), uint4(50, 200, 0, 255));
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, threaded_2x_smaller)
{
ImBuf *res = scale_2x_smaller(false, true);
const uchar4 *got = reinterpret_cast<const uchar4 *>(res->byte_data());
EXPECT_EQ(uint4(got[0]), uint4(191, 128, 64, 255));
EXPECT_EQ(uint4(got[1]), uint4(133, 55, 31, 16));
EXPECT_EQ(uint4(got[2]), uint4(55, 50, 48, 254));
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, bilinear_2x_smaller)
{
ImBuf *res = scale_2x_smaller(false, false);
const uchar4 *got = reinterpret_cast<const uchar4 *>(res->byte_data());
/* NOTE: #IMB_transform results in (191, 128, 64, 255), <same>,
* (55, 50, 48, 254) i.e. different rounding. */
EXPECT_EQ(uint4(got[0]), uint4(191, 127, 63, 255));
EXPECT_EQ(uint4(got[1]), uint4(133, 55, 31, 16));
EXPECT_EQ(uint4(got[2]), uint4(55, 50, 48, 253));
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, nearest_to_1x1)
{
ImBuf *res = scale_to_1x1(true, false);
const uchar4 *got = reinterpret_cast<const uchar4 *>(res->byte_data());
EXPECT_EQ(uint4(got[0]), uint4(0, 0, 0, 255));
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, threaded_to_1x1)
{
ImBuf *res = scale_to_1x1(false, true);
const uchar4 *got = reinterpret_cast<const uchar4 *>(res->byte_data());
EXPECT_EQ(uint4(got[0]), uint4(133, 55, 31, 16));
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, bilinear_to_1x1)
{
ImBuf *res = scale_to_1x1(false, false);
const uchar4 *got = reinterpret_cast<const uchar4 *>(res->byte_data());
EXPECT_EQ(uint4(got[0]), uint4(126, 78, 47, 174));
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, nearest_fractional_larger)
{
ImBuf *res = scale_fractional_larger(true, false);
const uchar4 *got = reinterpret_cast<const uchar4 *>(res->byte_data());
EXPECT_EQ(uint4(got[0 + 0 * res->x]), uint4(0, 0, 0, 255));
EXPECT_EQ(uint4(got[1 + 0 * res->x]), uint4(0, 0, 0, 255));
EXPECT_EQ(uint4(got[7 + 0 * res->x]), uint4(50, 200, 0, 255));
EXPECT_EQ(uint4(got[2 + 2 * res->x]), uint4(255, 0, 0, 255));
EXPECT_EQ(uint4(got[3 + 2 * res->x]), uint4(133, 55, 31, 13));
EXPECT_EQ(uint4(got[8 + 6 * res->x]), uint4(57, 0, 96, 252));
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, bilinear_fractional_larger)
{
ImBuf *res = scale_fractional_larger(false, false);
const uchar4 *got = reinterpret_cast<const uchar4 *>(res->byte_data());
EXPECT_EQ(uint4(got[0 + 0 * res->x]), uint4(0, 0, 0, 255));
EXPECT_EQ(uint4(got[1 + 0 * res->x]), uint4(127, 0, 0, 255));
EXPECT_EQ(uint4(got[7 + 0 * res->x]), uint4(52, 100, 16, 255));
EXPECT_EQ(uint4(got[2 + 2 * res->x]), uint4(235, 55, 51, 215));
EXPECT_EQ(uint4(got[3 + 2 * res->x]), uint4(153, 55, 35, 54));
EXPECT_EQ(uint4(got[8 + 5 * res->x]), uint4(57, 0, 91, 252));
EXPECT_EQ(uint4(got[0 + 6 * res->x]), uint4(164, 164, 0, 255));
EXPECT_EQ(uint4(got[7 + 6 * res->x]), uint4(55, 36, 57, 254));
EXPECT_EQ(uint4(got[8 + 6 * res->x]), uint4(56, 0, 73, 253));
IMB_freeImBuf(res);
}
static constexpr float EPS = 0.0001f;
TEST_F(ImBufScalingTest, nearest_2x_smaller_fl1)
{
ImBuf *res = scale_2x_smaller(true, false, 1);
const float *got = res->float_data();
EXPECT_NEAR(got[0], 0.0f, EPS);
EXPECT_NEAR(got[1], 2.5f, EPS);
EXPECT_NEAR(got[2], 5.0f, EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, nearest_2x_smaller_fl2)
{
ImBuf *res = scale_2x_smaller(true, false, 2);
const float2 *got = reinterpret_cast<const float2 *>(res->float_data());
EXPECT_V2_NEAR(got[0], float2(0.0f, 0.125f), EPS);
EXPECT_V2_NEAR(got[1], float2(2.5f, 2.625f), EPS);
EXPECT_V2_NEAR(got[2], float2(5.0f, 5.125f), EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, nearest_2x_smaller_fl3)
{
ImBuf *res = scale_2x_smaller(true, false, 3);
const float3 *got = reinterpret_cast<const float3 *>(res->float_data());
EXPECT_V3_NEAR(got[0], float3(0.0f, 0.125f, 0.25f), EPS);
EXPECT_V3_NEAR(got[1], float3(2.5f, 2.625f, 2.75f), EPS);
EXPECT_V3_NEAR(got[2], float3(5.0f, 5.125f, 5.25f), EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, nearest_2x_smaller_fl4)
{
ImBuf *res = scale_2x_smaller(true, false, 4);
const float4 *got = reinterpret_cast<const float4 *>(res->float_data());
EXPECT_V4_NEAR(got[0], float4(0.0f, 0.125f, 0.25f, 0.375f), EPS);
EXPECT_V4_NEAR(got[1], float4(2.5f, 2.625f, 2.75f, 2.875f), EPS);
EXPECT_V4_NEAR(got[2], float4(5.0f, 5.125f, 5.25f, 5.375f), EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, nearest_to_1x1_fl3)
{
ImBuf *res = scale_to_1x1(true, false, 3);
const float3 *got = reinterpret_cast<const float3 *>(res->float_data());
EXPECT_V3_NEAR(got[0], float3(0, 0.125f, 0.25f), EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, threaded_to_1x1_fl3)
{
ImBuf *res = scale_to_1x1(false, true, 3);
const float3 *got = reinterpret_cast<const float3 *>(res->float_data());
EXPECT_V3_NEAR(got[0], float3(3.375f, 3.5f, 3.625f), EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, bilinear_to_1x1_fl3)
{
ImBuf *res = scale_to_1x1(false, false, 3);
const float3 *got = reinterpret_cast<const float3 *>(res->float_data());
EXPECT_V3_NEAR(got[0], float3(3.36853f, 3.49353f, 3.61853f), EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, bilinear_2x_smaller_fl3)
{
ImBuf *res = scale_2x_smaller(false, false, 3);
const float3 *got = reinterpret_cast<const float3 *>(res->float_data());
EXPECT_V3_NEAR(got[0], float3(0.87270f, 0.99770f, 1.12270f), EPS);
EXPECT_V3_NEAR(got[1], float3(3.36853f, 3.49353f, 3.61853f), EPS);
EXPECT_V3_NEAR(got[2], float3(5.86435f, 5.98935f, 6.11435f), EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, bilinear_2x_smaller_fl4)
{
ImBuf *res = scale_2x_smaller(false, false, 4);
const float4 *got = reinterpret_cast<const float4 *>(res->float_data());
EXPECT_V4_NEAR(got[0], float4(0.87270f, 0.99770f, 1.12270f, 1.24770f), EPS);
EXPECT_V4_NEAR(got[1], float4(3.36853f, 3.49353f, 3.61853f, 3.74353f), EPS);
EXPECT_V4_NEAR(got[2], float4(5.86435f, 5.98935f, 6.11435f, 6.23935f), EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, threaded_2x_smaller_fl3)
{
ImBuf *res = scale_2x_smaller(false, true, 3);
const float3 *got = reinterpret_cast<const float3 *>(res->float_data());
EXPECT_V3_NEAR(got[0], float3(0.875f, 1.0f, 1.125f), EPS);
EXPECT_V3_NEAR(got[1], float3(3.375f, 3.5f, 3.625f), EPS);
EXPECT_V3_NEAR(got[2], float3(5.875f, 6.0f, 6.125f), EPS);
IMB_freeImBuf(res);
}
TEST_F(ImBufScalingTest, threaded_2x_smaller_fl4)
{
ImBuf *res = scale_2x_smaller(false, true, 4);
const float4 *got = reinterpret_cast<const float4 *>(res->float_data());
EXPECT_V4_NEAR(got[0], float4(0.875f, 1.0f, 1.125f, 1.25f), EPS);
EXPECT_V4_NEAR(got[1], float4(3.375f, 3.5f, 3.625f, 3.75f), EPS);
EXPECT_V4_NEAR(got[2], float4(5.875f, 6.0f, 6.125f, 6.25f), EPS);
IMB_freeImBuf(res);
}
} // namespace blender::imbuf::tests

View File

@@ -0,0 +1,161 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "testing/testing.h"
#include "BLI_color_types.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_quaternion_types.hh"
#include "IMB_imbuf.hh"
#include "BKE_gtest_base.hh"
namespace blender::imbuf::tests {
class ImBufTransformTest : public bke::BlenderGTestBase {};
static ImBuf *create_6x2_test_image()
{
ImBuf *img = IMB_allocImBuf(6, 2, ImBufFlags::ByteData);
ColorTheme4b *col = reinterpret_cast<ColorTheme4b *>(img->byte_data_for_write());
/* Source pixels are spelled out in 2x2 blocks below:
* nearest filter results in corner pixel from each block, bilinear
* is average of each block. */
col[0] = ColorTheme4b(0, 0, 0, 255);
col[1] = ColorTheme4b(255, 0, 0, 255);
col[6] = ColorTheme4b(255, 255, 0, 255);
col[7] = ColorTheme4b(255, 255, 255, 255);
col[2] = ColorTheme4b(133, 55, 31, 13);
col[3] = ColorTheme4b(133, 55, 31, 15);
col[8] = ColorTheme4b(133, 55, 31, 17);
col[9] = ColorTheme4b(133, 55, 31, 19);
col[4] = ColorTheme4b(50, 200, 0, 255);
col[5] = ColorTheme4b(55, 0, 32, 254);
col[10] = ColorTheme4b(56, 0, 64, 253);
col[11] = ColorTheme4b(57, 0, 96, 252);
return img;
}
static ImBuf *transform_2x_smaller(eIMBInterpolationFilterMode filter)
{
ImBuf *src = create_6x2_test_image();
ImBuf *dst = IMB_allocImBuf(3, 1, ImBufFlags::ByteData);
float3x3 matrix = math::from_scale<float3x3>(float3(2.0f));
IMB_transform(src, dst, IMB_TRANSFORM_MODE_REGULAR, filter, matrix, nullptr);
IMB_freeImBuf(src);
return dst;
}
static ImBuf *transform_fractional_larger(eIMBInterpolationFilterMode filter)
{
ImBuf *src = create_6x2_test_image();
ImBuf *dst = IMB_allocImBuf(9, 7, ImBufFlags::ByteData);
float3x3 matrix = math::from_scale<float3x3>(float3(6.0f / 9.0f, 2.0f / 7.0f, 1.0f));
IMB_transform(src, dst, IMB_TRANSFORM_MODE_REGULAR, filter, matrix, nullptr);
IMB_freeImBuf(src);
return dst;
}
TEST_F(ImBufTransformTest, nearest_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_NEAREST);
const ColorTheme4b *got = reinterpret_cast<ColorTheme4b *>(res->byte_data_for_write());
EXPECT_EQ(got[0], ColorTheme4b(255, 255, 255, 255));
EXPECT_EQ(got[1], ColorTheme4b(133, 55, 31, 19));
EXPECT_EQ(got[2], ColorTheme4b(57, 0, 96, 252));
IMB_freeImBuf(res);
}
TEST_F(ImBufTransformTest, box_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_BOX);
const ColorTheme4b *got = reinterpret_cast<const ColorTheme4b *>(res->byte_data());
/* At 2x reduction should be same as bilinear, save for some rounding errors. */
EXPECT_EQ(got[0], ColorTheme4b(191, 128, 64, 255));
EXPECT_EQ(got[1], ColorTheme4b(133, 55, 31, 16));
EXPECT_EQ(got[2], ColorTheme4b(54, 50, 48, 254));
IMB_freeImBuf(res);
}
TEST_F(ImBufTransformTest, bilinear_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_BILINEAR);
const ColorTheme4b *got = reinterpret_cast<const ColorTheme4b *>(res->byte_data());
EXPECT_EQ(got[0], ColorTheme4b(191, 128, 64, 255));
EXPECT_EQ(got[1], ColorTheme4b(133, 55, 31, 16));
EXPECT_EQ(got[2], ColorTheme4b(55, 50, 48, 254));
IMB_freeImBuf(res);
}
TEST_F(ImBufTransformTest, cubic_bspline_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_CUBIC_BSPLINE);
const ColorTheme4b *got = reinterpret_cast<const ColorTheme4b *>(res->byte_data());
EXPECT_EQ(got[0], ColorTheme4b(189, 126, 62, 250));
EXPECT_EQ(got[1], ColorTheme4b(134, 57, 33, 26));
EXPECT_EQ(got[2], ColorTheme4b(56, 49, 48, 249));
IMB_freeImBuf(res);
}
TEST_F(ImBufTransformTest, cubic_mitchell_2x_smaller)
{
ImBuf *res = transform_2x_smaller(IMB_FILTER_CUBIC_MITCHELL);
const ColorTheme4b *got = reinterpret_cast<const ColorTheme4b *>(res->byte_data());
EXPECT_EQ(got[0], ColorTheme4b(195, 130, 67, 255));
EXPECT_EQ(got[1], ColorTheme4b(132, 51, 28, 0));
EXPECT_EQ(got[2], ColorTheme4b(52, 52, 48, 255));
IMB_freeImBuf(res);
}
TEST_F(ImBufTransformTest, cubic_mitchell_fractional_larger)
{
ImBuf *res = transform_fractional_larger(IMB_FILTER_CUBIC_MITCHELL);
const ColorTheme4b *got = reinterpret_cast<const ColorTheme4b *>(res->byte_data());
EXPECT_EQ(got[0 + 0 * res->x], ColorTheme4b(0, 0, 0, 255));
EXPECT_EQ(got[1 + 0 * res->x], ColorTheme4b(127, 0, 0, 255));
EXPECT_EQ(got[7 + 0 * res->x], ColorTheme4b(49, 109, 13, 255));
EXPECT_EQ(got[2 + 2 * res->x], ColorTheme4b(236, 53, 50, 215));
EXPECT_EQ(got[3 + 2 * res->x], ColorTheme4b(155, 55, 35, 54));
EXPECT_EQ(got[8 + 6 * res->x], ColorTheme4b(57, 0, 98, 252));
IMB_freeImBuf(res);
}
TEST_F(ImBufTransformTest, nearest_very_large_scale)
{
/* Create 511x1 black image, with three middle pixels being red/green/blue. */
ImBuf *src = IMB_allocImBuf(511, 1, ImBufFlags::ByteData);
ColorTheme4b col_r = ColorTheme4b(255, 0, 0, 255);
ColorTheme4b col_g = ColorTheme4b(0, 255, 0, 255);
ColorTheme4b col_b = ColorTheme4b(0, 0, 255, 255);
ColorTheme4b col_0 = ColorTheme4b(0, 0, 0, 0);
ColorTheme4b *src_col = reinterpret_cast<ColorTheme4b *>(src->byte_data_for_write());
src_col[254] = col_r;
src_col[255] = col_g;
src_col[256] = col_b;
/* Create 3841x1 image, and scale the input image so that the three middle
* pixels cover almost all of it, except the rightmost pixel. */
ImBuf *res = IMB_allocImBuf(3841, 1, ImBufFlags::ByteData);
float3x3 matrix = math::from_loc_rot_scale<float3x3>(
float2(254, 0), 0.0f, float2(3.0f / 3840.0f, 1));
IMB_transform(src, res, IMB_TRANSFORM_MODE_REGULAR, IMB_FILTER_NEAREST, matrix, nullptr);
/* Check result: leftmost red, middle green, two rightmost pixels blue and black.
* If the transform code internally does not have enough precision while stepping
* through the scan-line, the rightmost side will not come out correctly. */
const ColorTheme4b *got = reinterpret_cast<const ColorTheme4b *>(res->byte_data());
EXPECT_EQ(got[0], col_r);
EXPECT_EQ(got[res->x / 2], col_g);
EXPECT_EQ(got[res->x - 2], col_b);
EXPECT_EQ(got[res->x - 1], col_0);
IMB_freeImBuf(src);
IMB_freeImBuf(res);
}
} // namespace blender::imbuf::tests