Composite opaque BGR rows via Zip() and skip per-pixel alpha division CompositeRow_Bgr2Bgra_NoBlend_NoClip() advanced through the source row by reassigning src_span via subspan(src_Bpp) once per pixel, which bounds-checks against the shrinking span's own size every iteration. The compiler cannot elide that check, because the checked offset is a derived induction variable rather than the loop's own. The caller knows the source format, so let it reinterpret the row into fixed-size pixel structs and hand the compositor a typed span. The row then becomes a single fxcrt::Zip() loop that compares sizes once and iterates checklessly. On the per-pixel BGRA no-blend path, the two callers already branch on src_alpha, so they now handle a fully opaque source directly. AlphaUnion() would be 255, the ratio 255, and AlphaMerge(..., 255) returns the source, so that case is a copy and skips the general path's division entirely. Opaque pixels dominate in real documents. Bug: 549552310 Change-Id: Iba8d9bf1326c8d9a539e1c4db614ee29405c83d3 Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/155530 Reviewed-by: Lei Zhang <thestig@chromium.org> Reviewed-by: Tom Sepez <tsepez@chromium.org> Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxge/dib/cfx_scanlinecompositor.cpp b/core/fxge/dib/cfx_scanlinecompositor.cpp index 2849e73..498f80a 100644 --- a/core/fxge/dib/cfx_scanlinecompositor.cpp +++ b/core/fxge/dib/cfx_scanlinecompositor.cpp
@@ -412,18 +412,43 @@ }); } +// Copies a source pixel into a BGRA destination, forcing it opaque. The +// source may carry an alpha channel or not; it is ignored either way. +template <typename SrcPixelStruct, typename DestPixelStruct> +void CopyPixelToOpaqueBgra(const SrcPixelStruct& input, + DestPixelStruct& output) { + output.blue = input.blue; + output.green = input.green; + output.red = input.red; + output.alpha = 255; +} + +template <typename DestPixelStruct, typename SrcPixelStruct> +void CopyRowToOpaqueBgra(pdfium::span<DestPixelStruct> dest_span, + pdfium::span<const SrcPixelStruct> src_span) { + for (auto [dest, src] : fxcrt::Zip(dest_span, src_span)) { + CopyPixelToOpaqueBgra(src, dest); + } +} + +// Picks the source pixel layout for the row above. A kBgr source has 3-byte +// pixels and a kBgrx source 4-byte ones, whose 4th byte is ignored. template <typename DestPixelStruct> void CompositeRow_Bgr2Bgra_NoBlend_NoClip( pdfium::span<DestPixelStruct> dest_span, pdfium::span<const uint8_t> src_span, - size_t src_Bpp) { - for (auto& dest : dest_span) { - dest.blue = src_span[0]; - dest.green = src_span[1]; - dest.red = src_span[2]; - dest.alpha = 255; - src_span = src_span.subspan(src_Bpp); + int src_Bpp) { + const size_t width = dest_span.size(); + if (src_Bpp == 3) { + CopyRowToOpaqueBgra(dest_span, + fxcrt::reinterpret_span<const FX_BGR_STRUCT<uint8_t>>( + src_span.first(width * 3))); + return; } + CHECK_EQ(src_Bpp, 4); + CopyRowToOpaqueBgra(dest_span, + fxcrt::reinterpret_span<const FX_BGRA_STRUCT<uint8_t>>( + src_span.first(width * 4))); } template <typename DestPixelStruct> @@ -613,7 +638,12 @@ for (auto [input, output] : fxcrt::Zip(src_span, dest_span)) { const uint8_t src_alpha = CompositePixelBgra2BgraCommon(input, /*clip=*/255, output); - if (src_alpha != 0) { + if (src_alpha == 255) { + // Fully opaque source: AlphaUnion() would be 255, the ratio 255, and + // AlphaMerge(..., 255) returns the source, so the general path in + // CompositePixelBgra2BgraNoBlend() reduces to a plain copy. + CopyPixelToOpaqueBgra(input, output); + } else if (src_alpha != 0) { CompositePixelBgra2BgraNoBlend(input, src_alpha, output); } } @@ -647,7 +677,10 @@ fxcrt::Zip(src_span, clip_span, dest_span)) { const uint8_t src_alpha = CompositePixelBgra2BgraCommon(input, clip, output); - if (src_alpha != 0) { + if (src_alpha == 255) { + // Same plain-copy reduction as the no-clip loop above. + CopyPixelToOpaqueBgra(input, output); + } else if (src_alpha != 0) { CompositePixelBgra2BgraNoBlend(input, src_alpha, output); } }