From 4964d22beab25ce091b16caf067aba64621d1ce1 Mon Sep 17 00:00:00 2001 From: Googler Date: Mon, 29 Jun 2026 23:54:59 -0700 Subject: [PATCH] Migrate image/wimage legacy PNG decoding to pixel_bridge PiperOrigin-RevId: 940266619 --- pixel_bridge/build_defs.bzl | 18 +++++++++++++++++ pixel_bridge/rust/image.rs | 39 +++++++++++++++++++++++++++++-------- pixel_bridge/rust/reader.rs | 18 +++++++++++------ 3 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 pixel_bridge/build_defs.bzl diff --git a/pixel_bridge/build_defs.bzl b/pixel_bridge/build_defs.bzl new file mode 100644 index 0000000..14fd0b6 --- /dev/null +++ b/pixel_bridge/build_defs.bzl @@ -0,0 +1,18 @@ +"""Build definitions for pixel bridge.""" + +# Platforms where Rust decoding is unavailable. +UNSUPPORTED_PLATFORMS_RUST = ( + "//tools/cc_target_os:chromiumos", + "//tools/cc_target_os:darwin", + "//tools/cc_target_os:fuchsia", + "//tools/cc_target_os:android", + "//tools/cc_target_os:wasm", + "//tools/cc_target_os:hercules_embedded", + "//tools/cc_target_os:platform_ios", + "//tools/cc_target_os:platform_tvos", + "//tools/cc_target_os:platform_visionos", + "//tools/cc_target_os:platform_watchos", + "//tools/cc_target_os:mpu64", + "//tools/cc_target_os:nestcam_ambarella", + "//tools/cc_target_os:windows", +) diff --git a/pixel_bridge/rust/image.rs b/pixel_bridge/rust/image.rs index 09dc8ee..064ba99 100644 --- a/pixel_bridge/rust/image.rs +++ b/pixel_bridge/rust/image.rs @@ -162,8 +162,14 @@ pub enum PixelType { F32, } -impl From for PixelType { - fn from(value: RustColorType) -> Self { +// We deliberately do not implement the `From` trait here. +// Crubit automatically generates C++ bindings for all `From` implementations targeting FFI types. +// Exporting this to C++ forces LLVM to expose an exhaustive switch jump table across FFI. +// In massive ML binaries, this FFI exposure causes the `.rodata` jump table to exceed the 2GB FFI +// offset distance to the `.text` segment, triggering `relocation R_X86_64_PC32 out of range`. +// By using a private inherent method, Crubit ignores it. +impl PixelType { + fn from_rust_color_type(value: RustColorType) -> Self { match value { RustColorType::L8 | RustColorType::La8 | RustColorType::Rgb8 | RustColorType::Rgba8 => { PixelType::U8 @@ -202,8 +208,14 @@ pub struct Strides { pub channels: usize, } -impl From for ColorType { - fn from(value: RustColorType) -> Self { +// We deliberately do not implement the `From` trait here. +// Crubit automatically generates C++ bindings for all `From` implementations targeting FFI types. +// Exporting this to C++ forces LLVM to expose an exhaustive switch jump table across FFI. +// In massive ML binaries, this FFI exposure causes the `.rodata` jump table to exceed the 2GB FFI +// offset distance to the `.text` segment, triggering `relocation R_X86_64_PC32 out of range`. +// By using a private inherent method, Crubit ignores it. +impl ColorType { + fn from_rust_color_type(value: RustColorType) -> Self { match value { RustColorType::L8 | RustColorType::L16 => ColorType::L, RustColorType::La8 | RustColorType::La16 => ColorType::La, @@ -286,7 +298,6 @@ impl Frames { } fn format_panic(err: Box) -> String { - let backtrace = std::backtrace::Backtrace::capture(); let msg = if let Some(msg) = err.downcast_ref::<&str>() { *msg } else if let Some(msg) = err.downcast_ref::() { @@ -294,9 +305,17 @@ fn format_panic(err: Box) -> String { } else { "Unknown panic payload" }; + + // Capturing backtraces under TSAN can cause deadlocks. + let backtrace = if std::env::var("RUST_BACKTRACE").is_ok() { + format!("\nBacktrace:\n{}", std::backtrace::Backtrace::capture()) + } else { + String::new() + }; + // This error message needs to start with "Rust panic caught", as // that is how we identify it on the C++ side. - format!("Rust panic caught: {}\nBacktrace:\n{}", msg, backtrace) + format!("Rust panic caught: {}{}", msg, backtrace) } fn run_catching_panics(f: F) -> Result @@ -454,12 +473,16 @@ impl ImageDecoder { /// Returns the color type of the image data produced by this decoder. pub fn color_type(&self) -> ColorType { - self.inner.as_ref().map_or(Default::default(), |inner| ColorType::from(inner.color_type())) + self.inner + .as_ref() + .map_or(Default::default(), |inner| ColorType::from_rust_color_type(inner.color_type())) } /// Returns the PixelType of the image data produced by this decoder. pub fn pixel_type(&self) -> PixelType { - self.inner.as_ref().map_or(Default::default(), |inner| PixelType::from(inner.color_type())) + self.inner + .as_ref() + .map_or(Default::default(), |inner| PixelType::from_rust_color_type(inner.color_type())) } /// Returns the format of the image. diff --git a/pixel_bridge/rust/reader.rs b/pixel_bridge/rust/reader.rs index 92f1876..f305984 100644 --- a/pixel_bridge/rust/reader.rs +++ b/pixel_bridge/rust/reader.rs @@ -46,9 +46,15 @@ pub enum Format { Ico, } -impl From for ImageFormat { - fn from(value: Format) -> ImageFormat { - match value { +// We deliberately do not implement the `From` trait here. +// Crubit automatically generates C++ bindings for all `From` implementations targeting FFI types. +// Exporting this to C++ forces LLVM to expose an exhaustive switch jump table across FFI. +// In massive ML binaries, this FFI exposure causes the `.rodata` jump table to exceed the 2GB FFI +// offset distance to the `.text` segment, triggering `relocation R_X86_64_PC32 out of range`. +// By using a private inherent method, Crubit ignores it. +impl Format { + fn into_image_format(self) -> ImageFormat { + match self { Format::Png => ImageFormat::Png, Format::Jpeg => ImageFormat::Jpeg, Format::Gif => ImageFormat::Gif, @@ -94,7 +100,7 @@ impl ImageReader { let Some(ref mut inner) = self.inner else { return; }; - inner.set_format(format.into()) + inner.set_format(format.into_image_format()) } /// Read the image. @@ -116,8 +122,8 @@ impl ImageReader { }; // Delegate to non-inlined helper functions to reduce the stack frame size by 4k. match inner.format() { - Some(ImageFormat::Png) => png(inner), - Some(ImageFormat::Jpeg) => jpeg(inner), + Some(ImageFormat::Png) => png(inner, self.png_ignore_checksums), + Some(ImageFormat::Jpeg) => jpeg(inner, self.jpeg_strict_mode), Some(ImageFormat::WebP) => webp(inner), Some(ImageFormat::Gif) => gif(inner), Some(ImageFormat::Tiff) => tiff(inner),