Fix save to image on Windows (#32914)

* Read pixels in same format (gl::RGBA) as the texture

Signed-off-by: crbrz <cristianb@gmail.com>

* Add read pixels test

Signed-off-by: crbrz <cristianb@gmail.com>

* Use patched surfman

Signed-off-by: crbrz <cristianb@gmail.com>

* Update surfman to 0.9.5

Signed-off-by: crbrz <cristianb@gmail.com>

---------

Signed-off-by: crbrz <cristianb@gmail.com>
This commit is contained in:
Cristian Brinza 2024-08-03 23:04:26 +03:00 committed by GitHub
parent bb176514c6
commit 7c2c383bb1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 114 additions and 11 deletions

View file

@ -46,5 +46,8 @@ webrender_api = { workspace = true }
webrender_traits = { workspace = true }
webxr = { git = "https://github.com/servo/webxr" }
[dev-dependencies]
surfman = { workspace = true }
[build-dependencies]
toml = "0.5"

View file

@ -2246,7 +2246,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
Some(Image {
width: img.width(),
height: img.height(),
format: PixelFormat::RGB8,
format: PixelFormat::RGBA8,
bytes: ipc::IpcSharedMemory::from_bytes(&img),
id: None,
cors_status: CorsStatus::Safe,
@ -2269,7 +2269,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
FramebufferUintLength::new(width),
FramebufferUintLength::new(height),
);
let dynamic_image = DynamicImage::ImageRgb8(img);
let dynamic_image = DynamicImage::ImageRgba8(img);
if let Err(e) = dynamic_image.write_to(&mut file, ImageFormat::Png) {
error!("Failed to save {} ({}).", path, e);
}

View file

@ -5,8 +5,8 @@
use std::rc::Rc;
use gleam::gl::{self, Gl};
use image::RgbImage;
use log::trace;
use image::RgbaImage;
use log::{trace, warn};
use servo_geometry::FramebufferUintLength;
pub struct RenderTargetInfo {
@ -105,7 +105,7 @@ impl RenderTargetInfo {
y: i32,
width: FramebufferUintLength,
height: FramebufferUintLength,
) -> RgbImage {
) -> RgbaImage {
let width = width.get() as usize;
let height = height.get() as usize;
// For some reason, OSMesa fails to render on the 3rd
@ -121,13 +121,17 @@ impl RenderTargetInfo {
y,
width as gl::GLsizei,
height as gl::GLsizei,
gl::RGB,
gl::RGBA,
gl::UNSIGNED_BYTE,
);
let gl_error = self.gl.get_error();
if gl_error != gl::NO_ERROR {
warn!("GL error code 0x{gl_error:x} set after read_pixels");
}
// flip image vertically (texture is upside down)
let orig_pixels = pixels.clone();
let stride = width * 3;
let stride = width * 4;
for y in 0..height {
let dst_start = y * stride;
let src_start = (height - y - 1) * stride;
@ -135,7 +139,7 @@ impl RenderTargetInfo {
pixels[dst_start..dst_start + stride].clone_from_slice(&src_slice[..stride]);
}
RgbImage::from_raw(width as u32, height as u32, pixels).expect("Flipping image failed!")
RgbaImage::from_raw(width as u32, height as u32, pixels).expect("Flipping image failed!")
}
}
@ -148,3 +152,57 @@ impl Drop for RenderTargetInfo {
self.gl.delete_framebuffers(&self.framebuffer_ids);
}
}
#[cfg(test)]
mod test {
use gleam::gl;
use image::Rgba;
use servo_geometry::FramebufferUintLength;
use surfman::{Connection, ContextAttributeFlags, ContextAttributes, Error, GLApi, GLVersion};
use super::RenderTargetInfo;
#[test]
#[allow(unsafe_code)]
fn test_read_pixels() -> Result<(), Error> {
let connection = Connection::new()?;
let adapter = connection.create_software_adapter()?;
let mut device = connection.create_device(&adapter)?;
let context_descriptor = device.create_context_descriptor(&ContextAttributes {
version: GLVersion::new(3, 0),
flags: ContextAttributeFlags::empty(),
})?;
let mut context = device.create_context(&context_descriptor, None)?;
let gl = match connection.gl_api() {
GLApi::GL => unsafe { gl::GlFns::load_with(|s| device.get_proc_address(&context, s)) },
GLApi::GLES => unsafe {
gl::GlesFns::load_with(|s| device.get_proc_address(&context, s))
},
};
device.make_context_current(&context)?;
{
const WIDTH: FramebufferUintLength = FramebufferUintLength::new(16);
const HEIGHT: FramebufferUintLength = FramebufferUintLength::new(16);
let render_target = RenderTargetInfo::new(gl, WIDTH, HEIGHT);
render_target.bind();
render_target
.gl
.clear_color(12.0 / 255.0, 34.0 / 255.0, 56.0 / 255.0, 78.0 / 255.0);
render_target.gl.clear(gl::COLOR_BUFFER_BIT);
let img = render_target.read_back_from_gpu(0, 0, WIDTH, HEIGHT);
assert_eq!(img.width(), WIDTH.get());
assert_eq!(img.height(), HEIGHT.get());
let expected_pixel: Rgba<u8> = Rgba([12, 34, 56, 78]);
assert!(img.pixels().all(|&p| p == expected_pixel));
}
device.destroy_context(&mut context)?;
Ok(())
}
}