From ace4d751495a5284585f7877a79d9f69e70e3c95 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 29 Jan 2024 11:31:30 +0100 Subject: Separate out OtherParams into Other{RGB,YUV}Params. --- src/lib/image.cc | 78 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 29 deletions(-) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 2588d9f21..c6e7b418c 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -642,7 +642,8 @@ struct TargetParams }; -struct OtherParams +/** Parameters of the other image (the one being blended onto the target) when target and other are RGB */ +struct OtherRGBParams { int start_x; int start_y; @@ -661,9 +662,20 @@ struct OtherParams }; +/** Parameters of the other image (the one being blended onto the target) when target and other are YUV */ +struct OtherYUVParams +{ + int start_x; + int start_y; + dcp::Size size; + uint8_t* const* data; + int const* stride; +}; + + template void -alpha_blend_onto_rgb24(TargetParams const& target, OtherParams const& other, int red, int blue, std::function get, int value_divisor) +alpha_blend_onto_rgb24(TargetParams const& target, OtherRGBParams const& other, int red, int blue, std::function get, int value_divisor) { /* Going onto RGB24. First byte is red, second green, third blue */ auto const alpha_divisor = other.alpha_divisor(); @@ -685,7 +697,7 @@ alpha_blend_onto_rgb24(TargetParams const& target, OtherParams const& other, int template void -alpha_blend_onto_bgra(TargetParams const& target, OtherParams const& other, int red, int blue, std::function get, int value_divisor) +alpha_blend_onto_bgra(TargetParams const& target, OtherRGBParams const& other, int red, int blue, std::function get, int value_divisor) { auto const alpha_divisor = other.alpha_divisor(); for (int ty = target.start_y, oy = other.start_y; ty < target.size.height && oy < other.size.height; ++ty, ++oy) { @@ -707,7 +719,7 @@ alpha_blend_onto_bgra(TargetParams const& target, OtherParams const& other, int template void -alpha_blend_onto_rgba(TargetParams const& target, OtherParams const& other, int red, int blue, std::function get, int value_divisor) +alpha_blend_onto_rgba(TargetParams const& target, OtherRGBParams const& other, int red, int blue, std::function get, int value_divisor) { auto const alpha_divisor = other.alpha_divisor(); for (int ty = target.start_y, oy = other.start_y; ty < target.size.height && oy < other.size.height; ++ty, ++oy) { @@ -729,7 +741,7 @@ alpha_blend_onto_rgba(TargetParams const& target, OtherParams const& other, int template void -alpha_blend_onto_rgb48le(TargetParams const& target, OtherParams const& other, int red, int blue, std::function get, int value_scale) +alpha_blend_onto_rgb48le(TargetParams const& target, OtherRGBParams const& other, int red, int blue, std::function get, int value_scale) { auto const alpha_divisor = other.alpha_divisor(); for (int ty = target.start_y, oy = other.start_y; ty < target.size.height && oy < other.size.height; ++ty, ++oy) { @@ -750,7 +762,7 @@ alpha_blend_onto_rgb48le(TargetParams const& target, OtherParams const& other, i template void -alpha_blend_onto_xyz12le(TargetParams const& target, OtherParams const& other, int red, int blue, std::function get, int value_divisor) +alpha_blend_onto_xyz12le(TargetParams const& target, OtherRGBParams const& other, int red, int blue, std::function get, int value_divisor) { auto const alpha_divisor = other.alpha_divisor(); auto conv = dcp::ColourConversion::srgb_to_xyz(); @@ -788,7 +800,7 @@ alpha_blend_onto_xyz12le(TargetParams const& target, OtherParams const& other, i static void -alpha_blend_onto_yuv420p(TargetParams const& target, OtherParams const& other, uint8_t* const* alpha_data, int const* alpha_stride) +alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other, uint8_t* const* alpha_data, int const* alpha_stride) { auto const ts = target.size; auto const os = other.size; @@ -825,7 +837,7 @@ alpha_blend_onto_yuv420p(TargetParams const& target, OtherParams const& other, u static void -alpha_blend_onto_yuv420p10(TargetParams const& target, OtherParams const& other, uint8_t* const* alpha_data, int const* alpha_stride) +alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& other, uint8_t* const* alpha_data, int const* alpha_stride) { auto const ts = target.size; auto const os = other.size; @@ -862,7 +874,7 @@ alpha_blend_onto_yuv420p10(TargetParams const& target, OtherParams const& other, static void -alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherParams const& other, uint8_t* const* alpha_data, int const* alpha_stride) +alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const& other, uint8_t* const* alpha_data, int const* alpha_stride) { auto const ts = target.size; auto const os = other.size; @@ -932,7 +944,7 @@ Image::alpha_blend (shared_ptr other, Position position) 0 }; - OtherParams other_params = { + OtherRGBParams other_rgb_params = { start_ox, start_oy, other->size(), @@ -941,6 +953,14 @@ Image::alpha_blend (shared_ptr other, Position position) other->pixel_format() == AV_PIX_FMT_RGBA64BE ? 8 : 4 }; + OtherYUVParams other_yuv_params = { + start_ox, + start_oy, + other->size(), + other->data(), + other->stride(), + }; + auto byteswap = [](uint16_t* p) { return (*p >> 8) | ((*p & 0xff) << 8); }; @@ -953,66 +973,66 @@ Image::alpha_blend (shared_ptr other, Position position) case AV_PIX_FMT_RGB24: target_params.bpp = 3; if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) { - alpha_blend_onto_rgb24(target_params, other_params, red, blue, byteswap, 256); + alpha_blend_onto_rgb24(target_params, other_rgb_params, red, blue, byteswap, 256); } else { - alpha_blend_onto_rgb24(target_params, other_params, red, blue, pass, 1); + alpha_blend_onto_rgb24(target_params, other_rgb_params, red, blue, pass, 1); } break; case AV_PIX_FMT_BGRA: target_params.bpp = 4; if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) { - alpha_blend_onto_bgra(target_params, other_params, red, blue, byteswap, 256); + alpha_blend_onto_bgra(target_params, other_rgb_params, red, blue, byteswap, 256); } else { - alpha_blend_onto_bgra(target_params, other_params, red, blue, pass, 1); + alpha_blend_onto_bgra(target_params, other_rgb_params, red, blue, pass, 1); } break; case AV_PIX_FMT_RGBA: target_params.bpp = 4; if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) { - alpha_blend_onto_rgba(target_params, other_params, red, blue, byteswap, 256); + alpha_blend_onto_rgba(target_params, other_rgb_params, red, blue, byteswap, 256); } else { - alpha_blend_onto_rgba(target_params, other_params, red, blue, pass, 1); + alpha_blend_onto_rgba(target_params, other_rgb_params, red, blue, pass, 1); } break; case AV_PIX_FMT_RGB48LE: target_params.bpp = 6; if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) { - alpha_blend_onto_rgb48le(target_params, other_params, red, blue, byteswap, 1); + alpha_blend_onto_rgb48le(target_params, other_rgb_params, red, blue, byteswap, 1); } else { - alpha_blend_onto_rgb48le(target_params, other_params, red, blue, pass, 256); + alpha_blend_onto_rgb48le(target_params, other_rgb_params, red, blue, pass, 256); } break; case AV_PIX_FMT_XYZ12LE: target_params.bpp = 6; if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) { - alpha_blend_onto_xyz12le(target_params, other_params, red, blue, byteswap, 256); + alpha_blend_onto_xyz12le(target_params, other_rgb_params, red, blue, byteswap, 256); } else { - alpha_blend_onto_xyz12le(target_params, other_params, red, blue, pass, 1); + alpha_blend_onto_xyz12le(target_params, other_rgb_params, red, blue, pass, 1); } break; case AV_PIX_FMT_YUV420P: { auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false); - other_params.data = yuv->data(); - other_params.stride = yuv->stride(); - alpha_blend_onto_yuv420p(target_params, other_params, other->data(), other->stride()); + other_yuv_params.data = yuv->data(); + other_yuv_params.stride = yuv->stride(); + alpha_blend_onto_yuv420p(target_params, other_yuv_params, other->data(), other->stride()); break; } case AV_PIX_FMT_YUV420P10: { auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false); - other_params.data = yuv->data(); - other_params.stride = yuv->stride(); - alpha_blend_onto_yuv420p10(target_params, other_params, other->data(), other->stride()); + other_yuv_params.data = yuv->data(); + other_yuv_params.stride = yuv->stride(); + alpha_blend_onto_yuv420p10(target_params, other_yuv_params, other->data(), other->stride()); break; } case AV_PIX_FMT_YUV422P9LE: case AV_PIX_FMT_YUV422P10LE: { auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false); - other_params.data = yuv->data(); - other_params.stride = yuv->stride(); - alpha_blend_onto_yuv422p9or10le(target_params, other_params, other->data(), other->stride()); + other_yuv_params.data = yuv->data(); + other_yuv_params.stride = yuv->stride(); + alpha_blend_onto_yuv422p9or10le(target_params, other_yuv_params, other->data(), other->stride()); break; } default: -- cgit v1.2.3 From 0403a4954ea84ace43c4248aae89af9a6384d7b2 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 29 Jan 2024 11:36:10 +0100 Subject: Put alpha_{data,stride} into OtherYUVParams. --- src/lib/image.cc | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index c6e7b418c..4042d5886 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -670,6 +670,9 @@ struct OtherYUVParams dcp::Size size; uint8_t* const* data; int const* stride; + + uint8_t* const* alpha_data; + int const* alpha_stride; }; @@ -800,7 +803,7 @@ alpha_blend_onto_xyz12le(TargetParams const& target, OtherRGBParams const& other static void -alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other, uint8_t* const* alpha_data, int const* alpha_stride) +alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other) { auto const ts = target.size; auto const os = other.size; @@ -813,7 +816,7 @@ alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other uint8_t* oY = other.data[0] + (oy * other.stride[0]) + other.start_x; uint8_t* oU = other.data[1] + (hoy * other.stride[1]) + other.start_x / 2; uint8_t* oV = other.data[2] + (hoy * other.stride[2]) + other.start_x / 2; - uint8_t* alpha = alpha_data[0] + (oy * alpha_stride[0]) + other.start_x * 4; + uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * 4; for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { float const a = float(alpha[3]) / 255; *tY = *oY * a + *tY * (1 - a); @@ -837,7 +840,7 @@ alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other static void -alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& other, uint8_t* const* alpha_data, int const* alpha_stride) +alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& other) { auto const ts = target.size; auto const os = other.size; @@ -850,7 +853,7 @@ alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& oth uint16_t* oY = reinterpret_cast(other.data[0] + (oy * other.stride[0])) + other.start_x; uint16_t* oU = reinterpret_cast(other.data[1] + (hoy * other.stride[1])) + other.start_x / 2; uint16_t* oV = reinterpret_cast(other.data[2] + (hoy * other.stride[2])) + other.start_x / 2; - uint8_t* alpha = alpha_data[0] + (oy * alpha_stride[0]) + other.start_x * 4; + uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * 4; for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { float const a = float(alpha[3]) / 255; *tY = *oY * a + *tY * (1 - a); @@ -874,7 +877,7 @@ alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& oth static void -alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const& other, uint8_t* const* alpha_data, int const* alpha_stride) +alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const& other) { auto const ts = target.size; auto const os = other.size; @@ -885,7 +888,7 @@ alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const uint16_t* oY = reinterpret_cast(other.data[0] + (oy * other.stride[0])) + other.start_x; uint16_t* oU = reinterpret_cast(other.data[1] + (oy * other.stride[1])) + other.start_x / 2; uint16_t* oV = reinterpret_cast(other.data[2] + (oy * other.stride[2])) + other.start_x / 2; - uint8_t* alpha = alpha_data[0] + (oy * alpha_stride[0]) + other.start_x * 4; + uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * 4; for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { float const a = float(alpha[3]) / 255; *tY = *oY * a + *tY * (1 - a); @@ -959,6 +962,8 @@ Image::alpha_blend (shared_ptr other, Position position) other->size(), other->data(), other->stride(), + nullptr, + nullptr }; auto byteswap = [](uint16_t* p) { @@ -1015,7 +1020,9 @@ Image::alpha_blend (shared_ptr other, Position position) auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false); other_yuv_params.data = yuv->data(); other_yuv_params.stride = yuv->stride(); - alpha_blend_onto_yuv420p(target_params, other_yuv_params, other->data(), other->stride()); + other_yuv_params.alpha_data = other->data(); + other_yuv_params.alpha_stride = other->stride(); + alpha_blend_onto_yuv420p(target_params, other_yuv_params); break; } case AV_PIX_FMT_YUV420P10: @@ -1023,7 +1030,9 @@ Image::alpha_blend (shared_ptr other, Position position) auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false); other_yuv_params.data = yuv->data(); other_yuv_params.stride = yuv->stride(); - alpha_blend_onto_yuv420p10(target_params, other_yuv_params, other->data(), other->stride()); + other_yuv_params.alpha_data = other->data(); + other_yuv_params.alpha_stride = other->stride(); + alpha_blend_onto_yuv420p10(target_params, other_yuv_params); break; } case AV_PIX_FMT_YUV422P9LE: @@ -1032,7 +1041,7 @@ Image::alpha_blend (shared_ptr other, Position position) auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false); other_yuv_params.data = yuv->data(); other_yuv_params.stride = yuv->stride(); - alpha_blend_onto_yuv422p9or10le(target_params, other_yuv_params, other->data(), other->stride()); + alpha_blend_onto_yuv422p9or10le(target_params, other_yuv_params); break; } default: -- cgit v1.2.3 From cf7df384d7698c0d75342e02b7efdbacf8611398 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 29 Jan 2024 11:38:39 +0100 Subject: Fix incorrect alpha step used for RGBA64 sources. --- src/lib/image.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 4042d5886..98c52c8e0 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -673,6 +673,7 @@ struct OtherYUVParams uint8_t* const* alpha_data; int const* alpha_stride; + int alpha_bpp; }; @@ -816,7 +817,7 @@ alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other uint8_t* oY = other.data[0] + (oy * other.stride[0]) + other.start_x; uint8_t* oU = other.data[1] + (hoy * other.stride[1]) + other.start_x / 2; uint8_t* oV = other.data[2] + (hoy * other.stride[2]) + other.start_x / 2; - uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * 4; + uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * other.alpha_bpp; for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { float const a = float(alpha[3]) / 255; *tY = *oY * a + *tY * (1 - a); @@ -832,7 +833,7 @@ alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other ++oU; ++oV; } - alpha += 4; + alpha += other.alpha_bpp; } } } @@ -853,7 +854,7 @@ alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& oth uint16_t* oY = reinterpret_cast(other.data[0] + (oy * other.stride[0])) + other.start_x; uint16_t* oU = reinterpret_cast(other.data[1] + (hoy * other.stride[1])) + other.start_x / 2; uint16_t* oV = reinterpret_cast(other.data[2] + (hoy * other.stride[2])) + other.start_x / 2; - uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * 4; + uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * other.alpha_bpp; for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { float const a = float(alpha[3]) / 255; *tY = *oY * a + *tY * (1 - a); @@ -869,7 +870,7 @@ alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& oth ++oU; ++oV; } - alpha += 4; + alpha += other.alpha_bpp; } } } @@ -888,7 +889,7 @@ alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const uint16_t* oY = reinterpret_cast(other.data[0] + (oy * other.stride[0])) + other.start_x; uint16_t* oU = reinterpret_cast(other.data[1] + (oy * other.stride[1])) + other.start_x / 2; uint16_t* oV = reinterpret_cast(other.data[2] + (oy * other.stride[2])) + other.start_x / 2; - uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * 4; + uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * other.alpha_bpp; for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { float const a = float(alpha[3]) / 255; *tY = *oY * a + *tY * (1 - a); @@ -904,7 +905,7 @@ alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const ++oU; ++oV; } - alpha += 4; + alpha += other.alpha_bpp; } } } @@ -963,7 +964,8 @@ Image::alpha_blend (shared_ptr other, Position position) other->data(), other->stride(), nullptr, - nullptr + nullptr, + other->pixel_format() == AV_PIX_FMT_RGBA64BE ? 8 : 4 }; auto byteswap = [](uint16_t* p) { -- cgit v1.2.3 From 150ee308a8a802df6e65a957bb256e891d12835e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 29 Jan 2024 12:16:55 +0100 Subject: Fix incorrect alpha channel scaling with RGBA64. --- src/lib/image.cc | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 98c52c8e0..366c1ae35 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -804,7 +804,7 @@ alpha_blend_onto_xyz12le(TargetParams const& target, OtherRGBParams const& other static void -alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other) +alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other, std::function get_alpha) { auto const ts = target.size; auto const os = other.size; @@ -819,7 +819,7 @@ alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other uint8_t* oV = other.data[2] + (hoy * other.stride[2]) + other.start_x / 2; uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * other.alpha_bpp; for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { - float const a = float(alpha[3]) / 255; + float const a = get_alpha(alpha); *tY = *oY * a + *tY * (1 - a); *tU = *oU * a + *tU * (1 - a); *tV = *oV * a + *tV * (1 - a); @@ -841,7 +841,7 @@ alpha_blend_onto_yuv420p(TargetParams const& target, OtherYUVParams const& other static void -alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& other) +alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& other, std::function get_alpha) { auto const ts = target.size; auto const os = other.size; @@ -856,7 +856,7 @@ alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& oth uint16_t* oV = reinterpret_cast(other.data[2] + (hoy * other.stride[2])) + other.start_x / 2; uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * other.alpha_bpp; for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { - float const a = float(alpha[3]) / 255; + float const a = get_alpha(alpha); *tY = *oY * a + *tY * (1 - a); *tU = *oU * a + *tU * (1 - a); *tV = *oV * a + *tV * (1 - a); @@ -878,7 +878,7 @@ alpha_blend_onto_yuv420p10(TargetParams const& target, OtherYUVParams const& oth static void -alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const& other) +alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const& other, std::function get_alpha) { auto const ts = target.size; auto const os = other.size; @@ -891,7 +891,7 @@ alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const uint16_t* oV = reinterpret_cast(other.data[2] + (oy * other.stride[2])) + other.start_x / 2; uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * other.alpha_bpp; for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { - float const a = float(alpha[3]) / 255; + float const a = get_alpha(alpha); *tY = *oY * a + *tY * (1 - a); *tU = *oU * a + *tU * (1 - a); *tV = *oV * a + *tV * (1 - a); @@ -976,6 +976,14 @@ Image::alpha_blend (shared_ptr other, Position position) return *p; }; + auto get_alpha_64be = [](uint8_t* p) { + return ((static_cast(p[6]) << 8) | p[7]) / 65535.0f; + }; + + auto get_alpha_byte = [](uint8_t* p) { + return p[3] / 255.0f; + }; + switch (_pixel_format) { case AV_PIX_FMT_RGB24: target_params.bpp = 3; @@ -1024,7 +1032,11 @@ Image::alpha_blend (shared_ptr other, Position position) other_yuv_params.stride = yuv->stride(); other_yuv_params.alpha_data = other->data(); other_yuv_params.alpha_stride = other->stride(); - alpha_blend_onto_yuv420p(target_params, other_yuv_params); + if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) { + alpha_blend_onto_yuv420p(target_params, other_yuv_params, get_alpha_64be); + } else { + alpha_blend_onto_yuv420p(target_params, other_yuv_params, get_alpha_byte); + } break; } case AV_PIX_FMT_YUV420P10: @@ -1034,7 +1046,11 @@ Image::alpha_blend (shared_ptr other, Position position) other_yuv_params.stride = yuv->stride(); other_yuv_params.alpha_data = other->data(); other_yuv_params.alpha_stride = other->stride(); - alpha_blend_onto_yuv420p10(target_params, other_yuv_params); + if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) { + alpha_blend_onto_yuv420p10(target_params, other_yuv_params, get_alpha_64be); + } else { + alpha_blend_onto_yuv420p10(target_params, other_yuv_params, get_alpha_byte); + } break; } case AV_PIX_FMT_YUV422P9LE: @@ -1043,7 +1059,13 @@ Image::alpha_blend (shared_ptr other, Position position) auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false); other_yuv_params.data = yuv->data(); other_yuv_params.stride = yuv->stride(); - alpha_blend_onto_yuv422p9or10le(target_params, other_yuv_params); + other_yuv_params.alpha_data = other->data(); + other_yuv_params.alpha_stride = other->stride(); + if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) { + alpha_blend_onto_yuv422p9or10le(target_params, other_yuv_params, get_alpha_64be); + } else { + alpha_blend_onto_yuv422p9or10le(target_params, other_yuv_params, get_alpha_byte); + } break; } default: -- cgit v1.2.3 From 9d1d1cea1cdf17b4cc2208800ca22288f979d3ec Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 29 Jan 2024 12:43:08 +0100 Subject: Support alpha blend onto YUV444P9LE and YUV444P10LE (68 and 70). --- src/lib/image.cc | 46 ++++++++++++++++++++++++++++++++++++++++++++++ test/image_test.cc | 4 ++++ 2 files changed, 50 insertions(+) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 366c1ae35..801b39b0f 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -911,6 +911,37 @@ alpha_blend_onto_yuv422p9or10le(TargetParams const& target, OtherYUVParams const } +static +void +alpha_blend_onto_yuv444p9or10le(TargetParams const& target, OtherYUVParams const& other, std::function get_alpha) +{ + auto const ts = target.size; + auto const os = other.size; + for (int ty = target.start_y, oy = other.start_y; ty < ts.height && oy < os.height; ++ty, ++oy) { + uint16_t* tY = reinterpret_cast(target.data[0] + (ty * target.stride[0])) + target.start_x; + uint16_t* tU = reinterpret_cast(target.data[1] + (ty * target.stride[1])) + target.start_x; + uint16_t* tV = reinterpret_cast(target.data[2] + (ty * target.stride[2])) + target.start_x; + uint16_t* oY = reinterpret_cast(other.data[0] + (oy * other.stride[0])) + other.start_x; + uint16_t* oU = reinterpret_cast(other.data[1] + (oy * other.stride[1])) + other.start_x; + uint16_t* oV = reinterpret_cast(other.data[2] + (oy * other.stride[2])) + other.start_x; + uint8_t* alpha = other.alpha_data[0] + (oy * other.alpha_stride[0]) + other.start_x * other.alpha_bpp; + for (int tx = target.start_x, ox = other.start_x; tx < ts.width && ox < os.width; ++tx, ++ox) { + float const a = get_alpha(alpha); + *tY = *oY * a + *tY * (1 - a); + *tU = *oU * a + *tU * (1 - a); + *tV = *oV * a + *tV * (1 - a); + ++tY; + ++oY; + ++tU; + ++tV; + ++oU; + ++oV; + alpha += other.alpha_bpp; + } + } +} + + void Image::alpha_blend (shared_ptr other, Position position) { @@ -1068,6 +1099,21 @@ Image::alpha_blend (shared_ptr other, Position position) } break; } + case AV_PIX_FMT_YUV444P9LE: + case AV_PIX_FMT_YUV444P10LE: + { + auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false); + other_yuv_params.data = yuv->data(); + other_yuv_params.stride = yuv->stride(); + other_yuv_params.alpha_data = other->data(); + other_yuv_params.alpha_stride = other->stride(); + if (other->pixel_format() == AV_PIX_FMT_RGBA64BE) { + alpha_blend_onto_yuv444p9or10le(target_params, other_yuv_params, get_alpha_64be); + } else { + alpha_blend_onto_yuv444p9or10le(target_params, other_yuv_params, get_alpha_byte); + } + break; + } default: throw PixelFormatError ("alpha_blend()", _pixel_format); } diff --git a/test/image_test.cc b/test/image_test.cc index 190e550ab..cad30813c 100644 --- a/test/image_test.cc +++ b/test/image_test.cc @@ -244,6 +244,8 @@ BOOST_AUTO_TEST_CASE (alpha_blend_test) alpha_blend_test_bgra_onto(AV_PIX_FMT_YUV420P10, "yuv420p10"); alpha_blend_test_bgra_onto(AV_PIX_FMT_YUV422P9LE, "yuv422p9le"); alpha_blend_test_bgra_onto(AV_PIX_FMT_YUV422P10LE, "yuv422p10le"); + alpha_blend_test_bgra_onto(AV_PIX_FMT_YUV444P9LE, "yuv444p9le"); + alpha_blend_test_bgra_onto(AV_PIX_FMT_YUV444P10LE, "yuv444p10le"); alpha_blend_test_rgba64be_onto(AV_PIX_FMT_RGB24, "rgb24"); alpha_blend_test_rgba64be_onto(AV_PIX_FMT_BGRA, "bgra"); @@ -253,6 +255,8 @@ BOOST_AUTO_TEST_CASE (alpha_blend_test) alpha_blend_test_rgba64be_onto(AV_PIX_FMT_YUV420P10, "yuv420p10"); alpha_blend_test_rgba64be_onto(AV_PIX_FMT_YUV422P9LE, "yuv422p9le"); alpha_blend_test_rgba64be_onto(AV_PIX_FMT_YUV422P10LE, "yuv422p10le"); + alpha_blend_test_rgba64be_onto(AV_PIX_FMT_YUV444P9LE, "yuv444p9le"); + alpha_blend_test_rgba64be_onto(AV_PIX_FMT_YUV444P10LE, "yuv444p10le"); } -- cgit v1.2.3 From 54ca161fd074a7ba17d9aed8479da189374eb64e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 1 Feb 2024 13:28:38 +0100 Subject: Add some asserts; sws_getContext() will fail if the image width or height are 0. --- src/lib/image.cc | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 801b39b0f..4c60fe888 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -339,6 +339,10 @@ Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_fo the input image alignment is not PADDED. */ DCPOMATIC_ASSERT (alignment() == Alignment::PADDED); + DCPOMATIC_ASSERT(size().width > 0); + DCPOMATIC_ASSERT(size().height > 0); + DCPOMATIC_ASSERT(out_size.width > 0); + DCPOMATIC_ASSERT(out_size.height > 0); auto scaled = make_shared(out_format, out_size, out_alignment); auto scale_context = sws_getContext ( -- cgit v1.2.3 From 092c23dde5ad89e8dc8d31b83154b5ac58f5a7f2 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 1 Feb 2024 13:28:49 +0100 Subject: Add an assert to check that sws_getContext() succeeded. --- src/lib/image.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/lib') diff --git a/src/lib/image.cc b/src/lib/image.cc index 4c60fe888..2167918f8 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -351,6 +351,8 @@ Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_fo (fast ? SWS_FAST_BILINEAR : SWS_BICUBIC) | SWS_ACCURATE_RND, 0, 0, 0 ); + DCPOMATIC_ASSERT(scale_context); + DCPOMATIC_ASSERT (yuv_to_rgb < dcp::YUVToRGB::COUNT); EnumIndexedVector lut; lut[dcp::YUVToRGB::REC601] = SWS_CS_ITU601; -- cgit v1.2.3 From 79ee0e5e79d8fb4a405d8b29827347d0243a17fe Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 1 Feb 2024 13:29:53 +0100 Subject: Don't emit subtitle images that have a zero dimension (#2743). They cause problems later when trying to blend them into the image. --- run/tests | 2 +- src/lib/player.cc | 7 +++++-- test/burnt_subtitle_test.cc | 22 ++++++++++++++++++++++ test/data | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) (limited to 'src/lib') diff --git a/run/tests b/run/tests index 12d59346d..4315e227f 100755 --- a/run/tests +++ b/run/tests @@ -3,7 +3,7 @@ # e.g. --run_tests=foo set -e -PRIVATE_GIT="59b62615a48a1f3a70e9c028b92dfba75c1719ba" +PRIVATE_GIT="f4b0c41b263a17cf33edae7565f16bd21eb80b65" if [ "$1" == "--check" ]; then shift 1 diff --git a/src/lib/player.cc b/src/lib/player.cc index ef7ecde5a..79b48ea71 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -921,7 +921,7 @@ Player::open_subtitles_for_frame (DCPTime time) const /* Bitmap subtitles */ for (auto i: j.bitmap) { - if (!i.image) { + if (!i.image || i.image->size().width == 0 || i.image->size().height == 0) { continue; } @@ -942,7 +942,10 @@ Player::open_subtitles_for_frame (DCPTime time) const /* String subtitles (rendered to an image) */ if (!j.string.empty()) { auto s = render_text(j.string, _video_container_size, time, vfr); - copy (s.begin(), s.end(), back_inserter (captions)); + copy_if(s.begin(), s.end(), back_inserter(captions), [](PositionImage const& image) { + return image.image->size().width && image.image->size().height; + }); + } } diff --git a/test/burnt_subtitle_test.cc b/test/burnt_subtitle_test.cc index 9c6863a1d..8d7dcd143 100644 --- a/test/burnt_subtitle_test.cc +++ b/test/burnt_subtitle_test.cc @@ -30,6 +30,7 @@ #include "lib/dcp_content.h" #include "lib/dcp_content_type.h" #include "lib/film.h" +#include "lib/ffmpeg_encoder.h" #include "lib/log_entry.h" #include "lib/ratio.h" #include "lib/text_content.h" @@ -184,3 +185,24 @@ BOOST_AUTO_TEST_CASE(burnt_subtitle_test_position) /* Should have a baseline 864 pixels from the top ((1 - 0.2) * 1080) */ check("bottom"); } + + +/* Bug #2743 */ +BOOST_AUTO_TEST_CASE(burn_empty_subtitle_test) +{ + Cleanup cl; + + auto content = content_factory("test/data/empty_sub.xml")[0]; + auto film = new_test_film2("burnt_empty_subtitle_test", { content }); + content->text[0]->set_use(true); + + auto job = make_shared(film, TranscodeJob::ChangedBehaviour::IGNORE); + auto file = boost::filesystem::path("build") / "test" / "burnt_empty_subtitle_test.mov"; + cl.add(file); + FFmpegEncoder encoder(film, job, file, ExportFormat::PRORES_4444, false, false, false, 23); + encoder.go(); + + cl.run(); +} + + diff --git a/test/data b/test/data index a81d5737e..704f76433 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit a81d5737e54105f95d3d0cbf602dd355b8814244 +Subproject commit 704f7643393caa29d77ddba138bff3642b273c46 -- cgit v1.2.3