ソースを参照

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.
Aliaksandr Kalenik 1 年間 前
コミット
aa6c008450

+ 83 - 0
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> Painter::create()
 {
     auto& context = Context::the();
@@ -66,6 +85,7 @@ OwnPtr<Painter> 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<float>(), bitmap, src_rect.to_type<float>(), 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());

+ 9 - 0
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<State, 1> m_state_stack;
 
     Program m_rectangle_program;
+    Program m_blit_program;
 };
 
 }

+ 19 - 3
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)
-{
-    // FIXME
+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: 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;
 }