From 9487f66eaa7d5a97fdf397fe5d3e797d47d8a0a8 Mon Sep 17 00:00:00 2001 From: Andrei Volykhin Date: Tue, 24 Jun 2025 14:15:50 +0300 Subject: [PATCH] webgl: Ignore pixel storage parameters for ImageBitmap source (#37635) Follow the WebGL specification and ignore the values of UNPACK_FLIP_Y, UNPACK_PREMULTIPLY_ALPHA, and UNPACK_COLORSPACE_CONVERSION if the TexImageSource is an ImageBitmap. https://registry.khronos.org/webgl/specs/latest/1.0/#6.10 Testing: There are no dedicated test results changes without the PR #37634 which unblocks new testing scenarios with premultiplied ImageBitmap from Blob/Canvas/Image sources. Signed-off-by: Andrei Volykhin --- .../script/dom/webgl2renderingcontext.rs | 9 +- .../script/dom/webglrenderingcontext.rs | 193 +++++++++++------- 2 files changed, 130 insertions(+), 72 deletions(-) diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index 43075fe502d..2c66cf865ac 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -3257,6 +3257,8 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingCont let size = Size2D::new(width, height); + let (alpha_treatment, y_axis_treatment) = self.base.get_current_unpack_state(false); + self.base.tex_image_2d( &texture, target, @@ -3267,7 +3269,12 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingCont border, unpacking_alignment, size, - TexSource::Pixels(TexPixels::from_array(buff, size)), + TexSource::Pixels(TexPixels::from_array( + buff, + size, + alpha_treatment, + y_axis_treatment, + )), ); Ok(()) diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index d6d4ce3bff6..f57abc7d7fe 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -507,6 +507,28 @@ impl WebGLRenderingContext { self.texture_packing_alignment.get() } + pub(crate) fn get_current_unpack_state( + &self, + premultiplied: bool, + ) -> (Option, YAxisTreatment) { + let settings = self.texture_unpacking_settings.get(); + let dest_premultiplied = settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA); + + let alpha_treatment = match (premultiplied, dest_premultiplied) { + (true, false) => Some(AlphaTreatment::Unmultiply), + (false, true) => Some(AlphaTreatment::Premultiply), + _ => None, + }; + + let y_axis_treatment = if settings.contains(TextureUnpacking::FLIP_Y_AXIS) { + YAxisTreatment::Flipped + } else { + YAxisTreatment::AsIs + }; + + (alpha_treatment, y_axis_treatment) + } + // LINEAR filtering may be forbidden when using WebGL extensions. // https://www.khronos.org/registry/webgl/extensions/OES_texture_float_linear/ fn validate_filterable_texture( @@ -552,7 +574,8 @@ impl WebGLRenderingContext { IpcSharedMemory::from_bytes(&pixels), size, PixelFormat::RGBA8, - true, + None, + YAxisTreatment::AsIs, )), ); @@ -578,6 +601,7 @@ impl WebGLRenderingContext { if !bitmap.origin_is_clean() { return Err(Error::Security); } + let Some(snapshot) = bitmap.bitmap_data().clone() else { return Ok(None); }; @@ -588,15 +612,32 @@ impl WebGLRenderingContext { SnapshotPixelFormat::RGBA => PixelFormat::RGBA8, SnapshotPixelFormat::BGRA => PixelFormat::BGRA8, }; - let premultiply = snapshot.alpha_mode().is_premultiplied(); - TexPixels::new(snapshot.to_ipc_shared_memory(), size, format, premultiply) + + // If the TexImageSource is an ImageBitmap, the values of + // UNPACK_FLIP_Y, UNPACK_PREMULTIPLY_ALPHA, and + // UNPACK_COLORSPACE_CONVERSION are to be ignored. + // Set alpha and y_axis treatment parameters such that no + // conversions will be made. + // + TexPixels::new( + snapshot.to_ipc_shared_memory(), + size, + format, + None, + YAxisTreatment::AsIs, + ) + }, + TexImageSource::ImageData(image_data) => { + let (alpha_treatment, y_axis_treatment) = self.get_current_unpack_state(false); + + TexPixels::new( + image_data.to_shared_memory(), + image_data.get_size(), + PixelFormat::RGBA8, + alpha_treatment, + y_axis_treatment, + ) }, - TexImageSource::ImageData(image_data) => TexPixels::new( - image_data.to_shared_memory(), - image_data.get_size(), - PixelFormat::RGBA8, - false, - ), TexImageSource::HTMLImageElement(image) => { let document = match self.canvas { HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(ref canvas) => { @@ -650,9 +691,11 @@ impl WebGLRenderingContext { }; let size = Size2D::new(img.metadata.width, img.metadata.height); - let data = IpcSharedMemory::from_bytes(img.first_frame().bytes); - TexPixels::new(data, size, img.format, false) + + let (alpha_treatment, y_axis_treatment) = self.get_current_unpack_state(false); + + TexPixels::new(data, size, img.format, alpha_treatment, y_axis_treatment) }, // TODO(emilio): Getting canvas data is implemented in CanvasRenderingContext2D, // but we need to refactor it moving it to `HTMLCanvasElement` and support @@ -661,18 +704,28 @@ impl WebGLRenderingContext { if !canvas.origin_is_clean() { return Err(Error::Security); } - if let Some(snapshot) = canvas.get_image_data() { - let snapshot = snapshot.as_ipc(); - let size = snapshot.size().cast(); - let format = match snapshot.format() { - SnapshotPixelFormat::RGBA => PixelFormat::RGBA8, - SnapshotPixelFormat::BGRA => PixelFormat::BGRA8, - }; - let premultiply = snapshot.alpha_mode().is_premultiplied(); - TexPixels::new(snapshot.to_ipc_shared_memory(), size, format, premultiply) - } else { + + let Some(snapshot) = canvas.get_image_data() else { return Ok(None); - } + }; + + let snapshot = snapshot.as_ipc(); + let size = snapshot.size().cast(); + let format = match snapshot.format() { + SnapshotPixelFormat::RGBA => PixelFormat::RGBA8, + SnapshotPixelFormat::BGRA => PixelFormat::BGRA8, + }; + + let (alpha_treatment, y_axis_treatment) = + self.get_current_unpack_state(snapshot.alpha_mode().is_premultiplied()); + + TexPixels::new( + snapshot.to_ipc_shared_memory(), + size, + format, + alpha_treatment, + y_axis_treatment, + ) }, TexImageSource::HTMLVideoElement(video) => { if !video.origin_is_clean() { @@ -689,8 +742,17 @@ impl WebGLRenderingContext { SnapshotPixelFormat::RGBA => PixelFormat::RGBA8, SnapshotPixelFormat::BGRA => PixelFormat::BGRA8, }; - let premultiply = snapshot.alpha_mode().is_premultiplied(); - TexPixels::new(snapshot.to_ipc_shared_memory(), size, format, premultiply) + + let (alpha_treatment, y_axis_treatment) = + self.get_current_unpack_state(snapshot.alpha_mode().is_premultiplied()); + + TexPixels::new( + snapshot.to_ipc_shared_memory(), + size, + format, + alpha_treatment, + y_axis_treatment, + ) }, })) } @@ -769,15 +831,6 @@ impl WebGLRenderingContext { ) ); - let settings = self.texture_unpacking_settings.get(); - let dest_premultiplied = settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA); - - let y_axis_treatment = if settings.contains(TextureUnpacking::FLIP_Y_AXIS) { - YAxisTreatment::Flipped - } else { - YAxisTreatment::AsIs - }; - let internal_format = self .extension_manager .get_effective_tex_internal_format(internal_format, data_type.as_gl_constant()); @@ -788,12 +841,6 @@ impl WebGLRenderingContext { match source { TexSource::Pixels(pixels) => { - let alpha_treatment = match (pixels.premultiplied, dest_premultiplied) { - (true, false) => Some(AlphaTreatment::Unmultiply), - (false, true) => Some(AlphaTreatment::Premultiply), - _ => None, - }; - // TODO(emilio): convert colorspace if requested. self.send_command(WebGLCommand::TexImage2D { target: target.as_gl_constant(), @@ -804,8 +851,8 @@ impl WebGLRenderingContext { data_type, effective_data_type, unpacking_alignment, - alpha_treatment, - y_axis_treatment, + alpha_treatment: pixels.alpha_treatment, + y_axis_treatment: pixels.y_axis_treatment, pixel_format: pixels.pixel_format, data: pixels.data.into(), }); @@ -873,21 +920,6 @@ impl WebGLRenderingContext { return self.webgl_error(InvalidOperation); } - let settings = self.texture_unpacking_settings.get(); - let dest_premultiplied = settings.contains(TextureUnpacking::PREMULTIPLY_ALPHA); - - let alpha_treatment = match (pixels.premultiplied, dest_premultiplied) { - (true, false) => Some(AlphaTreatment::Unmultiply), - (false, true) => Some(AlphaTreatment::Premultiply), - _ => None, - }; - - let y_axis_treatment = if settings.contains(TextureUnpacking::FLIP_Y_AXIS) { - YAxisTreatment::Flipped - } else { - YAxisTreatment::AsIs - }; - let effective_data_type = self .extension_manager .effective_type(data_type.as_gl_constant()); @@ -903,8 +935,8 @@ impl WebGLRenderingContext { data_type, effective_data_type, unpacking_alignment, - alpha_treatment, - y_axis_treatment, + alpha_treatment: pixels.alpha_treatment, + y_axis_treatment: pixels.y_axis_treatment, pixel_format: pixels.pixel_format, data: pixels.data.into(), }); @@ -1581,9 +1613,7 @@ impl WebGLRenderingContext { } let size = Size2D::new(width, height); - let buff = IpcSharedMemory::from_bytes(data); - let pixels = TexPixels::from_array(buff, size); - let data = pixels.data; + let data = IpcSharedMemory::from_bytes(data); handle_potential_webgl_error!( self, @@ -1646,9 +1676,7 @@ impl WebGLRenderingContext { Err(_) => return, }; - let buff = IpcSharedMemory::from_bytes(data); - let pixels = TexPixels::from_array(buff, Size2D::new(width, height)); - let data = pixels.data; + let data = IpcSharedMemory::from_bytes(data); self.send_command(WebGLCommand::CompressedTexSubImage2D { target: target.as_gl_constant(), @@ -4533,6 +4561,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContex let size = Size2D::new(width, height); + let (alpha_treatment, y_axis_treatment) = self.get_current_unpack_state(false); + self.tex_image_2d( &texture, target, @@ -4543,7 +4573,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContex border, unpacking_alignment, size, - TexSource::Pixels(TexPixels::from_array(buff, size)), + TexSource::Pixels(TexPixels::from_array( + buff, + size, + alpha_treatment, + y_axis_treatment, + )), ); Ok(()) @@ -4703,6 +4738,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContex }; } + let (alpha_treatment, y_axis_treatment) = self.get_current_unpack_state(false); + self.tex_sub_image_2d( texture, target, @@ -4712,7 +4749,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContex format, data_type, unpacking_alignment, - TexPixels::from_array(buff, Size2D::new(width, height)), + TexPixels::from_array( + buff, + Size2D::new(width, height), + alpha_treatment, + y_axis_treatment, + ), ); Ok(()) } @@ -5056,7 +5098,8 @@ pub(crate) struct TexPixels { data: IpcSharedMemory, size: Size2D, pixel_format: Option, - premultiplied: bool, + alpha_treatment: Option, + y_axis_treatment: YAxisTreatment, } impl TexPixels { @@ -5064,22 +5107,30 @@ impl TexPixels { data: IpcSharedMemory, size: Size2D, pixel_format: PixelFormat, - premultiplied: bool, + alpha_treatment: Option, + y_axis_treatment: YAxisTreatment, ) -> Self { Self { data, size, pixel_format: Some(pixel_format), - premultiplied, + alpha_treatment, + y_axis_treatment, } } - pub(crate) fn from_array(data: IpcSharedMemory, size: Size2D) -> Self { + pub(crate) fn from_array( + data: IpcSharedMemory, + size: Size2D, + alpha_treatment: Option, + y_axis_treatment: YAxisTreatment, + ) -> Self { Self { data, size, pixel_format: None, - premultiplied: false, + alpha_treatment, + y_axis_treatment, } }