TEST(FlatInput, UpdatedData) { const int width = 2; const int height = 4; float data[width * height] = { 0.0, 1.0, 0.5, 0.5, 0.7, 0.2, 1.0, 0.6, }; float out_data[width * height]; EffectChainTester tester(NULL, width, height); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height); input->set_pixel_data(data); tester.get_chain()->add_input(input); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); expect_equal(data, out_data, width, height); data[6] = 0.3; input->invalidate_pixel_data(); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); expect_equal(data, out_data, width, height); }
TEST(FlatInput, PBO) { const int width = 3; const int height = 2; float data[width * height] = { 0.0, 1.0, 0.5, 0.5, 0.5, 0.2, }; float expected_data[4 * width * height] = { 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.5, 0.5, 0.5, 1.0, 0.5, 0.5, 0.5, 1.0, 0.2, 0.2, 0.2, 1.0, }; float out_data[4 * width * height]; GLuint pbo; glGenBuffers(1, &pbo); glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo); glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, width * height * sizeof(float), data, GL_STREAM_DRAW); glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); EffectChainTester tester(NULL, width, height); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height); input->set_pixel_data((float *)BUFFER_OFFSET(0), pbo); tester.get_chain()->add_input(input); tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR); expect_equal(expected_data, out_data, 4 * width, height); glDeleteBuffers(1, &pbo); }
TEST(PaddingEffectTest, WhiteBorderColor) { float data[2 * 2] = { 1.0f, 0.5f, 0.8f, 0.3f, }; float expected_data[4 * 4] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, 1.0f, 1.0f, 0.8f, 0.3f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, }; float out_data[4 * 4]; EffectChainTester tester(NULL, 4, 4); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, 2, 2); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *effect = tester.get_chain()->add_effect(new PaddingEffect()); CHECK(effect->set_int("width", 4)); CHECK(effect->set_int("height", 4)); CHECK(effect->set_float("left", 1.0f)); CHECK(effect->set_float("top", 1.0f)); RGBATuple border_color(1.0f, 1.0f, 1.0f, 1.0f); CHECK(effect->set_vec4("border_color", (float *)&border_color)); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED); expect_equal(expected_data, out_data, 4, 4); }
TEST(FlatInput, Pitch) { const int pitch = 3; const int width = 2; const int height = 4; float data[pitch * height] = { 0.0, 1.0, 999.0f, 0.5, 0.5, 999.0f, 0.7, 0.2, 999.0f, 1.0, 0.6, 999.0f, }; float expected_data[4 * width * height] = { 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.5, 0.5, 0.5, 1.0, 0.7, 0.7, 0.7, 1.0, 0.2, 0.2, 0.2, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6, 0.6, 0.6, 1.0, }; float out_data[4 * width * height]; EffectChainTester tester(NULL, width, height); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height); input->set_pitch(pitch); input->set_pixel_data(data); tester.get_chain()->add_input(input); tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR); expect_equal(expected_data, out_data, 4 * width, height); }
TEST(PaddingEffectTest, BorderColorIsInLinearGamma) { float data[4 * 1] = { 0.2f, 0.4f, 0.6f, 0.8f, }; float expected_data[4 * 2] = { 0.5005, 0.7051, 0.8677, 0.7998, // Pixel from data[]. 0.5005, 0.7051, 0.8677, 0.7998, // Pixel from the border color. }; float out_data[4 * 2]; EffectChainTester tester(NULL, 1, 2); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_RGBA_PREMULTIPLIED_ALPHA, GL_FLOAT, 1, 1); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *effect = tester.get_chain()->add_effect(new PaddingEffect()); CHECK(effect->set_int("width", 1)); CHECK(effect->set_int("height", 2)); CHECK(effect->set_float("left", 0.0f)); CHECK(effect->set_float("top", 0.0f)); RGBATuple border_color(0.2f, 0.4f, 0.6f, 0.8f); // Same as the pixel in data[]. CHECK(effect->set_vec4("border_color", (float *)&border_color)); tester.run(out_data, GL_RGBA, COLORSPACE_REC_601_625, GAMMA_REC_601, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); expect_equal(expected_data, out_data, 4, 2); }
TEST(PaddingEffectTest, BorderOffsetTopAndBottom) { float data[2 * 2] = { 1.0f, 0.5f, 0.8f, 0.3f, }; float expected_data[4 * 4] = { 0.0f, 0.000f, 0.000f, 0.0f, 0.0f, 0.750f, 0.375f, 0.0f, 0.0f, 0.800f, 0.300f, 0.0f, 0.0f, 0.200f, 0.075f, 0.0f, // Repeated pixels, 25% opacity. }; float out_data[4 * 4]; EffectChainTester tester(NULL, 4, 4); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, 2, 2); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *effect = tester.get_chain()->add_effect(new PaddingEffect()); CHECK(effect->set_int("width", 4)); CHECK(effect->set_int("height", 4)); CHECK(effect->set_float("left", 1.0f)); CHECK(effect->set_float("top", 1.0f)); CHECK(effect->set_float("border_offset_top", 0.25f)); CHECK(effect->set_float("border_offset_bottom", 0.25f)); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED); expect_equal(expected_data, out_data, 4, 4); }
TEST(PaddingEffectTest, BorderOffsetLeftAndRight) { float data[3 * 2] = { 1.0f, 0.5f, 0.6f, 0.8f, 0.3f, 0.2f, }; float expected_data[4 * 2] = { 0.750f, 0.5f, 0.3f, 0.0f, 0.600f, 0.3f, 0.1f, 0.0f }; float out_data[4 * 2]; EffectChainTester tester(NULL, 4, 2); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, 3, 2); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *effect = tester.get_chain()->add_effect(new PaddingEffect()); CHECK(effect->set_int("width", 4)); CHECK(effect->set_int("height", 2)); CHECK(effect->set_float("left", 0.0f)); CHECK(effect->set_float("top", 0.0f)); CHECK(effect->set_float("border_offset_left", 0.25f)); CHECK(effect->set_float("border_offset_right", -0.5f)); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED); expect_equal(expected_data, out_data, 4, 2); }
TEST(PaddingEffectTest, NonIntegerOffset) { float data[4 * 1] = { 0.25f, 0.50f, 0.75f, 1.0f, }; float expected_data[5 * 2] = { 0.1875f, 0.4375f, 0.6875f, 0.9375f, 0.25f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, }; float out_data[5 * 2]; EffectChainTester tester(NULL, 5, 2); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, 4, 1); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *effect = tester.get_chain()->add_effect(new PaddingEffect()); CHECK(effect->set_int("width", 5)); CHECK(effect->set_int("height", 2)); CHECK(effect->set_float("left", 0.25f)); CHECK(effect->set_float("top", 0.0f)); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED); expect_equal(expected_data, out_data, 5, 2); }
TEST(PaddingEffectTest, AlphaIsCorrectEvenWithNonLinearInputsAndOutputs) { float data[2 * 1] = { 1.0f, 0.8f, }; float expected_data[4 * 4] = { 1.0f, 1.0f, 1.0f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.8f, 0.8f, 0.8f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f, }; float out_data[4 * 4]; EffectChainTester tester(NULL, 1, 4); ImageFormat format; format.color_space = COLORSPACE_REC_601_625; format.gamma_curve = GAMMA_REC_709; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, 1, 2); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *effect = tester.get_chain()->add_effect(new PaddingEffect()); CHECK(effect->set_int("width", 1)); CHECK(effect->set_int("height", 4)); CHECK(effect->set_float("left", 0.0f)); CHECK(effect->set_float("top", 1.0f)); RGBATuple border_color(1.0f, 1.0f, 1.0f, 0.5f); CHECK(effect->set_vec4("border_color", (float *)&border_color)); tester.run(out_data, GL_RGBA, COLORSPACE_REC_601_625, GAMMA_REC_709, OUTPUT_ALPHA_FORMAT_POSTMULTIPLIED); expect_equal(expected_data, out_data, 4, 4); }
TEST(PaddingEffectTest, DifferentXAndYOffset) { float data[1 * 1] = { 1.0f }; float expected_data[3 * 3] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, }; float out_data[3 * 3]; EffectChainTester tester(NULL, 3, 3); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, 1, 1); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *effect = tester.get_chain()->add_effect(new PaddingEffect()); CHECK(effect->set_int("width", 3)); CHECK(effect->set_int("height", 3)); CHECK(effect->set_float("left", 2.0f)); CHECK(effect->set_float("top", 1.0f)); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED); expect_equal(expected_data, out_data, 3, 3); }
void MltInput::set_pixel_data(const unsigned char* data) { assert(input); if (isRGB) { FlatInput* flat = (FlatInput*) input; flat->set_pixel_data(data); } else { YCbCrInput* ycbcr = (YCbCrInput*) input; ycbcr->set_pixel_data(0, data); ycbcr->set_pixel_data(1, &data[m_width * m_height]); ycbcr->set_pixel_data(2, &data[m_width * m_height + (m_width / m_ycbcr_format.chroma_subsampling_x * m_height / m_ycbcr_format.chroma_subsampling_y)]); } }
TEST(FlatInput, ExternalTexture) { const int size = 5; float data[3 * size] = { 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.7, 0.0, 0.3, 0.7, }; float expected_data[4 * size] = { 0.0, 0.0, 0.0, 1.0, 0.5, 0.0, 0.0, 1.0, 0.0, 0.5, 0.0, 1.0, 0.0, 0.0, 0.7, 1.0, 0.0, 0.3, 0.7, 1.0, }; float out_data[4 * size]; EffectChainTester tester(NULL, 1, size, FORMAT_RGB, COLORSPACE_sRGB, GAMMA_LINEAR); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; ResourcePool pool; GLuint tex = pool.create_2d_texture(GL_RGB8, 1, size); check_error(); glBindTexture(GL_TEXTURE_2D, tex); check_error(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); check_error(); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); check_error(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, size, GL_RGB, GL_FLOAT, data); check_error(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); check_error(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); check_error(); FlatInput *input = new FlatInput(format, FORMAT_RGB, GL_FLOAT, 1, size); input->set_texture_num(tex); tester.get_chain()->add_input(input); tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR); pool.release_2d_texture(tex); expect_equal(expected_data, out_data, 4, size); }
TEST(ResampleEffectTest, DownscaleByTwoGetsCorrectPixelCenters) { const int size = 5; // This isn't a perfect dot, since the Lanczos filter has a slight // sharpening effect; the most important thing is that we have kept // the texel center right (everything is nicely symmetric). // The approximate magnitudes have been checked against ImageMagick. float expected_data[size * size] = { 0.0045, -0.0067, -0.0598, -0.0067, 0.0045, -0.0067, 0.0099, 0.0886, 0.0099, -0.0067, -0.0598, 0.0886, 0.7930, 0.0886, -0.0598, -0.0067, 0.0099, 0.0886, 0.0099, -0.0067, 0.0045, -0.0067, -0.0598, -0.0067, 0.0045, }; float data[size * size * 4], out_data[size * size]; for (int y = 0; y < size * 2; ++y) { for (int x = 0; x < size * 2; ++x) { float weight = lanczos((x - size + 0.5f) * 0.5f, 3.0f); weight *= lanczos((y - size + 0.5f) * 0.5f, 3.0f); data[y * (size * 2) + x] = weight; } } EffectChainTester tester(NULL, size, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size * 2, size * 2); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect()); ASSERT_TRUE(resample_effect->set_int("width", size)); ASSERT_TRUE(resample_effect->set_int("height", size)); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); expect_equal(expected_data, out_data, size, size); }
TEST(ResampleEffectTest, HeavyResampleGetsSumRight) { // Do only one resample pass, more specifically the last one, which goes to // our fp32 output. This allows us to analyze the precision without intermediate // fp16 rounding. const int swidth = 1, sheight = 1280; const int dwidth = 1, dheight = 64; float data[swidth * sheight], out_data[dwidth * dheight], expected_data[dwidth * dheight]; for (int y = 0; y < sheight; ++y) { for (int x = 0; x < swidth; ++x) { data[y * swidth + x] = 1.0f; } } for (int y = 0; y < dheight; ++y) { for (int x = 0; x < dwidth; ++x) { expected_data[y * dwidth + x] = 1.0f; } } EffectChainTester tester(NULL, dwidth, dheight, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, GL_RGBA32F); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, swidth, sheight); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect()); ASSERT_TRUE(resample_effect->set_int("width", dwidth)); ASSERT_TRUE(resample_effect->set_int("height", dheight)); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); // Require that we are within 10-bit accuracy. Note that this is for // one pass only; some cards that don't have correct fp32 -> fp16 // rounding in the intermediate framebuffers will go outside this after // a 2D resize. This limit is tight enough that it will be good enough // for 8-bit accuracy, though. expect_equal(expected_data, out_data, dwidth, dheight, 0.5 / 1023.0); }
TEST(ResampleEffectTest, UpscaleByTwoGetsCorrectPixelCenters) { const int size = 5; float data[size * size] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, }; float expected_data[size * size * 4], out_data[size * size * 4]; for (int y = 0; y < size * 2; ++y) { for (int x = 0; x < size * 2; ++x) { float weight = lanczos((x - size + 0.5f) * 0.5f, 3.0f); weight *= lanczos((y - size + 0.5f) * 0.5f, 3.0f); expected_data[y * (size * 2) + x] = weight; } } EffectChainTester tester(NULL, size * 2, size * 2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size, size); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect()); ASSERT_TRUE(resample_effect->set_int("width", size * 2)); ASSERT_TRUE(resample_effect->set_int("height", size * 2)); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); expect_equal(expected_data, out_data, size * 2, size * 2); }
TEST(ResampleEffectTest, UpscaleByThreeGetsCorrectPixelCenters) { const int size = 5; float data[size * size] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, }; float out_data[size * size * 9]; EffectChainTester tester(NULL, size * 3, size * 3, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR); ImageFormat format; format.color_space = COLORSPACE_sRGB; format.gamma_curve = GAMMA_LINEAR; FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, size, size); input->set_pixel_data(data); tester.get_chain()->add_input(input); Effect *resample_effect = tester.get_chain()->add_effect(new ResampleEffect()); ASSERT_TRUE(resample_effect->set_int("width", size * 3)); ASSERT_TRUE(resample_effect->set_int("height", size * 3)); tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR); // We only bother checking that the middle pixel is still correct, // and that symmetry holds. EXPECT_FLOAT_EQ(1.0, out_data[7 * (size * 3) + 7]); for (unsigned y = 0; y < size * 3; ++y) { for (unsigned x = 0; x < size * 3; ++x) { EXPECT_FLOAT_EQ(out_data[y * (size * 3) + x], out_data[(size * 3 - y - 1) * (size * 3) + x]); EXPECT_FLOAT_EQ(out_data[y * (size * 3) + x], out_data[y * (size * 3) + (size * 3 - x - 1)]); } } }