From aa6c008450bb470cd3ef77bdcb40a0acf613cced Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Thu, 2 Nov 2023 01:18:07 +0100 Subject: [PATCH] LibAccelGfx+LibWeb: Implement draw_scaled_bitmap() Very basic implementation of command to paint bitmap. In the future we should reuse loaded textures across repaints whenever it is possible. --- Userland/Libraries/LibAccelGfx/Painter.cpp | 83 +++++++++++++++++++ Userland/Libraries/LibAccelGfx/Painter.h | 9 ++ .../Painting/PaintingCommandExecutorGPU.cpp | 20 ++++- 3 files changed, 110 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibAccelGfx/Painter.cpp b/Userland/Libraries/LibAccelGfx/Painter.cpp index df35ec85a2..7d2a6d2672 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.cpp +++ b/Userland/Libraries/LibAccelGfx/Painter.cpp @@ -57,6 +57,25 @@ void main() { } )"; +char const* blit_vertex_shader_source = R"( +attribute vec4 aVertexPosition; +attribute vec2 aTextureCoord; +varying vec2 vTextureCoord; +void main() { + gl_Position = aVertexPosition; + vTextureCoord = aTextureCoord; +} +)"; + +char const* blit_fragment_shader_source = R"( +precision mediump float; +varying vec2 vTextureCoord; +uniform sampler2D uSampler; +void main() { + gl_FragColor = texture2D(uSampler, vTextureCoord); +} +)"; + OwnPtr Painter::create() { auto& context = Context::the(); @@ -66,6 +85,7 @@ OwnPtr Painter::create() Painter::Painter(Context& context) : m_context(context) , m_rectangle_program(Program::create(vertex_shader_source, solid_color_fragment_shader_source)) + , m_blit_program(Program::create(blit_vertex_shader_source, blit_fragment_shader_source)) { m_state_stack.empend(State()); } @@ -125,6 +145,69 @@ void Painter::fill_rect(Gfx::FloatRect rect, Gfx::Color color) glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } +void Painter::draw_scaled_bitmap(Gfx::IntRect const& dest_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, ScalingMode scaling_mode) +{ + draw_scaled_bitmap(dest_rect.to_type(), bitmap, src_rect.to_type(), scaling_mode); +} + +static Gfx::FloatRect to_texture_space(Gfx::FloatRect rect, Gfx::IntSize image_size) +{ + auto x = rect.x() / image_size.width(); + auto y = rect.y() / image_size.height(); + auto width = rect.width() / image_size.width(); + auto height = rect.height() / image_size.height(); + + return { x, y, width, height }; +} + +static GLenum to_gl_scaling_mode(Painter::ScalingMode scaling_mode) +{ + switch (scaling_mode) { + case Painter::ScalingMode::NearestNeighbor: + return GL_NEAREST; + case Painter::ScalingMode::Bilinear: + return GL_LINEAR; + default: + VERIFY_NOT_REACHED(); + } +} + +void Painter::draw_scaled_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::FloatRect const& src_rect, ScalingMode scaling_mode) +{ + m_blit_program.use(); + + GLuint texture; + + // FIXME: We should reuse textures across repaints if possible. + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap.width(), bitmap.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, bitmap.scanline(0)); + + GLenum scaling_mode_gl = to_gl_scaling_mode(scaling_mode); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, scaling_mode_gl); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, scaling_mode_gl); + + auto vertices = rect_to_vertices(to_clip_space(transform().map(dst_rect))); + auto texture_coordinates = rect_to_vertices(to_texture_space(src_rect, bitmap.size())); + + GLuint vertex_position_attribute = m_blit_program.get_attribute_location("aVertexPosition"); + glVertexAttribPointer(vertex_position_attribute, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vertices.data()); + glEnableVertexAttribArray(vertex_position_attribute); + + GLuint texture_coord_attribute = m_blit_program.get_attribute_location("aTextureCoord"); + glVertexAttribPointer(texture_coord_attribute, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), texture_coordinates.data()); + glEnableVertexAttribArray(texture_coord_attribute); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDeleteTextures(1, &texture); +} + void Painter::save() { m_state_stack.append(state()); diff --git a/Userland/Libraries/LibAccelGfx/Painter.h b/Userland/Libraries/LibAccelGfx/Painter.h index 48937c09b7..2be0a966d0 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.h +++ b/Userland/Libraries/LibAccelGfx/Painter.h @@ -37,6 +37,14 @@ public: void fill_rect(Gfx::FloatRect, Gfx::Color); void fill_rect(Gfx::IntRect, Gfx::Color); + enum class ScalingMode { + NearestNeighbor, + Bilinear, + }; + + void draw_scaled_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap const&, Gfx::FloatRect const& src_rect, ScalingMode = ScalingMode::NearestNeighbor); + void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const&, Gfx::IntRect const& src_rect, ScalingMode = ScalingMode::NearestNeighbor); + void set_canvas(Canvas& canvas) { m_canvas = canvas; } void flush(); @@ -56,6 +64,7 @@ private: Vector m_state_stack; Program m_rectangle_program; + Program m_blit_program; }; } diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp index 4cac0265c4..e93a7ceacc 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp @@ -36,9 +36,25 @@ CommandResult PaintingCommandExecutorGPU::fill_rect(Gfx::IntRect const& rect, Co return CommandResult::Continue; } -CommandResult PaintingCommandExecutorGPU::draw_scaled_bitmap(Gfx::IntRect const&, Gfx::Bitmap const&, Gfx::IntRect const&, float, Gfx::Painter::ScalingMode) +CommandResult PaintingCommandExecutorGPU::draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, float, Gfx::Painter::ScalingMode scaling_mode) { - // FIXME + // FIXME: We should avoid using Gfx::Painter specific enums in painting commands + AccelGfx::Painter::ScalingMode accel_scaling_mode; + switch (scaling_mode) { + case Gfx::Painter::ScalingMode::NearestNeighbor: + case Gfx::Painter::ScalingMode::BoxSampling: + case Gfx::Painter::ScalingMode::SmoothPixels: + case Gfx::Painter::ScalingMode::None: + accel_scaling_mode = AccelGfx::Painter::ScalingMode::NearestNeighbor; + break; + case Gfx::Painter::ScalingMode::BilinearBlend: + accel_scaling_mode = AccelGfx::Painter::ScalingMode::Bilinear; + break; + default: + VERIFY_NOT_REACHED(); + } + + painter().draw_scaled_bitmap(dst_rect, bitmap, src_rect, accel_scaling_mode); return CommandResult::Continue; }