From 3e7061da40a9a15c2535bbfb2b51d387b4e21aeb Mon Sep 17 00:00:00 2001 From: Luke Wilde Date: Thu, 23 Oct 2025 20:24:14 +0100 Subject: [PATCH] LibWeb/WebGL: Respect UNPACK_FLIP_Y_WEBGL pixel storage parameter When this is true, we have to vertically flip TexImageSource provided images before uploading them. Fixes several graphical glitches on Google Maps. Fixes globe being upside down on Shopify's homepage. Likely fixes more websites. --- .../WebGL/WebGL2RenderingContextImpl.cpp | 9 +++++++++ .../WebGL/WebGLRenderingContextBase.cpp | 19 +++++++++++++++++-- .../LibWeb/WebGL/WebGLRenderingContextBase.h | 11 ++++++++++- .../WebGL/WebGLRenderingContextImpl.cpp | 9 +++++++++ 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp index 3cc094a114e..f04851eafa5 100644 --- a/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp @@ -2849,6 +2849,8 @@ JS::Value WebGL2RenderingContextImpl::get_parameter(WebIDL::UnsignedLong pname) glGetBooleanvRobustANGLE(GL_TRANSFORM_FEEDBACK_PAUSED, 1, nullptr, &result); return JS::Value(result == GL_TRUE); } + case UNPACK_FLIP_Y_WEBGL: + return JS::Value(m_unpack_flip_y); default: dbgln("Unknown WebGL parameter name: {:x}", pname); set_error(GL_INVALID_ENUM); @@ -3145,6 +3147,13 @@ void WebGL2RenderingContextImpl::link_program(GC::Root program) void WebGL2RenderingContextImpl::pixel_storei(WebIDL::UnsignedLong pname, WebIDL::Long param) { m_context->make_current(); + + switch (pname) { + case UNPACK_FLIP_Y_WEBGL: + m_unpack_flip_y = param != GL_FALSE; + return; + } + glPixelStorei(pname, param); } diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp index 1eca3c14e8f..046bd938583 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp @@ -12,6 +12,7 @@ extern "C" { #include } +#include #include #include #include @@ -20,10 +21,12 @@ extern "C" { #include #include +#include #include #include #include #include +#include namespace Web::WebGL { @@ -178,8 +181,20 @@ Optional WebGLRenderingContextBase: // FIXME: Respect unpackColorSpace auto color_space = SkColorSpace::MakeSRGB(); auto image_info = SkImageInfo::Make(width, height, skia_format, SkAlphaType::kPremul_SkAlphaType, color_space); - SkPixmap const pixmap(image_info, buffer.data(), buffer_pitch.value()); - bitmap->sk_image()->readPixels(pixmap, 0, 0); + auto surface = SkSurfaces::WrapPixels(image_info, buffer.data(), buffer_pitch.value()); + auto surface_canvas = surface->getCanvas(); + auto dst_rect = Gfx::to_skia_rect(Gfx::Rect { 0, 0, width, height }); + + // The first pixel transferred from the source to the WebGL implementation corresponds to the upper left corner of + // the source. This behavior is modified by the UNPACK_FLIP_Y_WEBGL pixel storage parameter, except for ImageBitmap + // arguments, as described in the abovementioned section. + if (m_unpack_flip_y && !source.has>()) { + surface_canvas->translate(0, dst_rect.height()); + surface_canvas->scale(1, -1); + } + + surface_canvas->drawImageRect(bitmap->sk_image(), dst_rect, Gfx::to_skia_sampling_options(Gfx::ScalingMode::NearestNeighbor)); + return ConvertedTexture { .buffer = move(buffer), .width = width, diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h index 1f1d045d2a6..6ece0ef8b02 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h @@ -14,6 +14,8 @@ namespace Web::WebGL { +static constexpr int UNPACK_FLIP_Y_WEBGL = 0x9240; + // NOTE: This is the Variant created by the IDL wrapper generator, and needs to be updated accordingly. using TexImageSource = Variant, GC::Root, GC::Root, GC::Root, GC::Root, GC::Root>; @@ -107,7 +109,14 @@ public: int width { 0 }; int height { 0 }; }; - static Optional read_and_pixel_convert_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Optional destination_width = OptionalNone {}, Optional destination_height = OptionalNone {}); + Optional read_and_pixel_convert_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Optional destination_width = OptionalNone {}, Optional destination_height = OptionalNone {}); + +protected: + // UNPACK_FLIP_Y_WEBGL of type boolean + // If set, then during any subsequent calls to texImage2D or texSubImage2D, the source data is flipped along + // the vertical axis, so that conceptually the last row is the first one transferred. The initial value is false. + // Any non-zero value is interpreted as true. + bool m_unpack_flip_y { false }; }; } diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp index c179c9ab307..3848ab26f87 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp @@ -1501,6 +1501,8 @@ JS::Value WebGLRenderingContextImpl::get_parameter(WebIDL::UnsignedLong pname) set_error(GL_INVALID_ENUM); return JS::js_null(); } + case UNPACK_FLIP_Y_WEBGL: + return JS::Value(m_unpack_flip_y); default: dbgln("Unknown WebGL parameter name: {:x}", pname); set_error(GL_INVALID_ENUM); @@ -1793,6 +1795,13 @@ void WebGLRenderingContextImpl::link_program(GC::Root program) void WebGLRenderingContextImpl::pixel_storei(WebIDL::UnsignedLong pname, WebIDL::Long param) { m_context->make_current(); + + switch (pname) { + case UNPACK_FLIP_Y_WEBGL: + m_unpack_flip_y = param != GL_FALSE; + return; + } + glPixelStorei(pname, param); }