Explorar el Código

WindowServer: Support windows with alpha channels. And per-WSWindow opacity.

This patch also adds a Format concept to GraphicsBitmap. For now there are
only two formats: RGB32 and RGBA32. Windows with alpha channel have their
backing stores created in the RGBA32 format.

Use this to make Terminal windows semi-transparent for that comfy rice look.
There is one problem here, in that window compositing overdraw incurs
multiple passes of blending of the same pixels. This leads to a mismatch in
opacity which is obviously not good. I will work on this in a later patch.

The alpha blending is currently straight C++. It should be relatively easy
to optimize this using SSE instructions.

For now I'm just happy with the cute effect. :^)
Andreas Kling hace 6 años
padre
commit
9b71307d49

+ 1 - 1
Applications/About/main.cpp

@@ -17,7 +17,7 @@ int main(int argc, char** argv)
     window->set_main_widget(widget);
 
     auto* icon_label = new GLabel(widget);
-    icon_label->set_icon(GraphicsBitmap::load_from_file("/res/icons/Serenity.rgb", { 32, 32 }));
+    icon_label->set_icon(GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/Serenity.rgb", { 32, 32 }));
     icon_label->set_relative_rect(
         widget->rect().center().x() - 16,
         10,

+ 4 - 4
Applications/FileManager/DirectoryView.cpp

@@ -12,9 +12,9 @@
 DirectoryView::DirectoryView(GWidget* parent)
     : GWidget(parent)
 {
-    m_directory_icon = GraphicsBitmap::load_from_file("/res/icons/folder16.rgb", { 16, 16 });
-    m_file_icon = GraphicsBitmap::load_from_file("/res/icons/file16.rgb", { 16, 16 });
-    m_symlink_icon = GraphicsBitmap::load_from_file("/res/icons/link16.rgb", { 16, 16 });
+    m_directory_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/folder16.rgb", { 16, 16 });
+    m_file_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/file16.rgb", { 16, 16 });
+    m_symlink_icon = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/link16.rgb", { 16, 16 });
 
     m_scrollbar = new GScrollBar(Orientation::Vertical, this);
     m_scrollbar->set_step(4);
@@ -142,7 +142,7 @@ void DirectoryView::paint_event(GPaintEvent&)
             Rect name_rect(icon_rect.right() + horizontal_padding, y, 100, item_height());
             Rect size_rect(name_rect.right() + horizontal_padding, y, 64, item_height());
             painter.fill_rect(row_rect(painted_item_index), i % 2 ? Color(210, 210, 210) : Color::White);
-            painter.blit_with_alpha(icon_rect.location(), icon_for(entry), { 0, 0, icon_size, icon_size });
+            painter.blit(icon_rect.location(), icon_for(entry), { 0, 0, icon_size, icon_size });
             painter.draw_text(name_rect, entry.name, TextAlignment::CenterLeft, Color::Black);
             if (should_show_size_for(entry))
                 painter.draw_text(size_rect, pretty_byte_size(entry.size), TextAlignment::CenterRight, Color::Black);

+ 1 - 1
Applications/Launcher/main.cpp

@@ -38,7 +38,7 @@ public:
         : GButton(parent)
         , m_executable_path(exec_path)
     {
-        set_icon(GraphicsBitmap::load_from_file(icon_path, { 32, 32 }));
+        set_icon(GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, icon_path, { 32, 32 }));
         resize(50, 50);
         on_click = [this] (GButton&) {
             pid_t child_pid = fork();

+ 10 - 6
Applications/Terminal/Terminal.cpp

@@ -143,7 +143,7 @@ unsigned parse_uint(const String& str, bool& ok)
 
 static inline Color lookup_color(unsigned color)
 {
-    return xterm_colors[color];
+    return Color::from_rgb(xterm_colors[color]);
 }
 
 void Terminal::escape$m(const Vector<unsigned>& params)
@@ -668,10 +668,14 @@ void Terminal::keydown_event(GKeyEvent& event)
 }
 
 void Terminal::paint_event(GPaintEvent&)
-{
-    Rect rect { 0, 0, m_pixel_width, m_pixel_height };
+{   
     Painter painter(*this);
 
+    if (m_needs_background_fill) {
+        m_needs_background_fill = false;
+        painter.fill_rect(rect(), Color(Color::Black).with_alpha(255 * m_opacity));
+    }
+
     if (m_rows_to_scroll_backing_store && m_rows_to_scroll_backing_store < m_rows) {
         int first_scanline = m_inset;
         int second_scanline = m_inset + (m_rows_to_scroll_backing_store * m_line_height);
@@ -695,7 +699,7 @@ void Terminal::paint_event(GPaintEvent&)
         line.dirty = false;
         bool has_only_one_background_color = line.has_only_one_background_color();
         if (has_only_one_background_color) {
-            painter.fill_rect(row_rect(row), lookup_color(line.attributes[0].background_color));
+            painter.fill_rect(row_rect(row), lookup_color(line.attributes[0].background_color).with_alpha(255 * m_opacity));
         }
         for (word column = 0; column < m_columns; ++column) {
             bool should_reverse_fill_for_cursor = m_in_active_window && row == m_cursor_row && column == m_cursor_column;
@@ -704,7 +708,7 @@ void Terminal::paint_event(GPaintEvent&)
             auto character_rect = glyph_rect(row, column);
             if (!has_only_one_background_color || should_reverse_fill_for_cursor) {
                 auto cell_rect = character_rect.inflated(0, m_line_spacing);
-                painter.fill_rect(cell_rect, lookup_color(should_reverse_fill_for_cursor ? attribute.foreground_color : attribute.background_color));
+                painter.fill_rect(cell_rect, lookup_color(should_reverse_fill_for_cursor ? attribute.foreground_color : attribute.background_color).with_alpha(255 * m_opacity));
             }
             if (ch == ' ')
                 continue;
@@ -718,7 +722,7 @@ void Terminal::paint_event(GPaintEvent&)
     }
 
     if (m_belling)
-        painter.draw_rect(rect, Color::Red);
+        painter.draw_rect(rect(), Color::Red);
 }
 
 void Terminal::set_window_title(String&& title)

+ 3 - 0
Applications/Terminal/Terminal.h

@@ -144,4 +144,7 @@ private:
     RetainPtr<Font> m_font;
 
     GNotifier m_notifier;
+
+    float m_opacity { 0.8f };
+    bool m_needs_background_fill { true };
 };

+ 1 - 0
Applications/Terminal/main.cpp

@@ -89,6 +89,7 @@ int main(int argc, char** argv)
     window->set_should_exit_app_on_close(true);
 
     Terminal terminal(ptm_fd);
+    window->set_has_alpha_channel(true);
     window->set_main_widget(&terminal);
     window->move_to(300, 300);
     window->show();

+ 1 - 1
LibGUI/GButton.cpp

@@ -36,7 +36,7 @@ void GButton::paint_event(GPaintEvent&)
             icon_location.move_by(1, 1);
         }
         if (m_icon) {
-            painter.blit_with_alpha(icon_location, *m_icon, m_icon->rect());
+            painter.blit(icon_location, *m_icon, m_icon->rect());
             painter.draw_text(content_rect, caption(), TextAlignment::Center, Color::Black);
         } else {
             painter.draw_text(content_rect, caption(), TextAlignment::Center, Color::Black);

+ 1 - 1
LibGUI/GLabel.cpp

@@ -31,7 +31,7 @@ void GLabel::paint_event(GPaintEvent&)
         painter.fill_rect({ 0, 0, width(), height() }, background_color());
     if (m_icon) {
         auto icon_location = rect().center().translated(-(m_icon->width() / 2), -(m_icon->height() / 2));
-        painter.blit_with_alpha(icon_location, *m_icon, m_icon->rect());
+        painter.blit(icon_location, *m_icon, m_icon->rect());
     }
     if (!text().is_empty())
         painter.draw_text({ 0, 0, width(), height() }, text(), m_text_alignment, foreground_color());

+ 2 - 2
LibGUI/GWidget.h

@@ -128,8 +128,8 @@ private:
     OwnPtr<GLayout> m_layout;
 
     Rect m_relative_rect;
-    Color m_background_color { 0xffffff };
-    Color m_foreground_color { 0x000000 };
+    Color m_background_color;
+    Color m_foreground_color;
     RetainPtr<Font> m_font;
 
     SizePolicy m_horizontal_size_policy { SizePolicy::Fill };

+ 21 - 0
LibGUI/GWindow.cpp

@@ -59,6 +59,8 @@ void GWindow::show()
     request.type = WSAPI_ClientMessage::Type::CreateWindow;
     request.window_id = m_window_id;
     request.window.rect = m_rect_when_windowless;
+    request.window.has_alpha_channel = m_has_alpha_channel;
+    request.window.opacity = m_opacity_when_windowless;
     ASSERT(m_title_when_windowless.length() < sizeof(request.text));
     strcpy(request.text, m_title_when_windowless.characters());
     request.text_length = m_title_when_windowless.length();
@@ -280,3 +282,22 @@ void GWindow::set_global_cursor_tracking_widget(GWidget* widget)
     //        Maybe there could be a response that includes the current cursor location as of enabling.
     GEventLoop::main().post_message_to_server(request);
 }
+
+void GWindow::set_has_alpha_channel(bool value)
+{
+    ASSERT(!m_window_id);
+    m_has_alpha_channel = value;
+}
+
+void GWindow::set_opacity(float opacity)
+{
+    m_opacity_when_windowless = opacity;
+    if (!m_window_id)
+        return;
+    WSAPI_ClientMessage request;
+    request.type = WSAPI_ClientMessage::Type::SetWindowOpacity;
+    request.window_id = m_window_id;
+    request.window.opacity = opacity;
+    m_opacity_when_windowless = opacity;
+    GEventLoop::main().post_message_to_server(request);
+}

+ 6 - 1
LibGUI/GWindow.h

@@ -15,6 +15,9 @@ public:
 
     static GWindow* from_window_id(int);
 
+    void set_has_alpha_channel(bool);
+    void set_opacity(float);
+
     int window_id() const { return m_window_id; }
 
     String title() const;
@@ -66,13 +69,15 @@ private:
 
     RetainPtr<GraphicsBitmap> m_backing;
     int m_window_id { 0 };
-    bool m_is_active { false };
+    float m_opacity_when_windowless { 1.0f };
     GWidget* m_main_widget { nullptr };
     GWidget* m_focused_widget { nullptr };
     WeakPtr<GWidget> m_global_cursor_tracking_widget;
     Rect m_rect_when_windowless;
     String m_title_when_windowless;
     Vector<Rect> m_pending_paint_event_rects;
+    bool m_is_active { false };
     bool m_should_exit_app_on_close { false };
+    bool m_has_alpha_channel { false };
 };
 

+ 1 - 1
SharedGraphics/Color.cpp

@@ -23,5 +23,5 @@ Color::Color(NamedColor named)
     default: ASSERT_NOT_REACHED(); break;
     }
 
-    m_value = (rgb.r << 16) | (rgb.g << 8) | rgb.b;
+    m_value = 0xff000000 | (rgb.r << 16) | (rgb.g << 8) | rgb.b;
 }

+ 30 - 8
SharedGraphics/Color.h

@@ -26,26 +26,48 @@ public:
 
     Color() { }
     Color(NamedColor);
-    Color(byte r, byte g, byte b) : m_value((r << 16) | (g << 8) | b) { }
-    Color(RGBA32 rgba) : m_value(rgba) { }
+    Color(byte r, byte g, byte b) : m_value(0xff000000 | (r << 16) | (g << 8) | b) { }
+    Color(byte r, byte g, byte b, byte a) : m_value((a << 24) | (r << 16) | (g << 8) | b) { }
+
+    static Color from_rgb(unsigned rgb) { return Color(rgb | 0xff000000); }
+    static Color from_rgba(unsigned rgba) { return Color(rgba); }
 
     byte red() const { return (m_value >> 16) & 0xff; }
     byte green() const { return (m_value >> 8) & 0xff; }
     byte blue() const { return m_value & 0xff; }
     byte alpha() const { return (m_value >> 24) & 0xff; }
 
-    Color blend(Color source) const
+    void set_alpha(byte value)
     {
-        RGBA32 redblue1 = ((0x100u - source.alpha()) * (m_value & 0xff00ff)) >> 8;
-        RGBA32 redblue2 = (source.alpha() * (source.m_value & 0xff00ff)) >> 8;
-        RGBA32 green1  = ((0x100u - source.alpha()) * (m_value & 0x00ff00)) >> 8;
-        RGBA32 green2  = (source.alpha() * (source.m_value & 0x00ff00)) >> 8;
-        return Color(((redblue1 | redblue2) & 0xff00ff) + ((green1 | green2) & 0x00ff00));
+        m_value &= 0x00ffffff;
+        m_value |= value << 24;
     }
 
+    Color with_alpha(byte alpha)
+    {
+        return Color((m_value & 0x00ffffff) | alpha << 24);
+    }
+
+    Color blend(Color source) const
+    {
+        if (!alpha() || source.alpha() == 255)
+            return source;
+
+        if (!source.alpha())
+            return *this;
+
+        int d = 255 * (alpha() + source.alpha()) - alpha() * source.alpha();
+        byte r = (red() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.red()) / d;
+        byte g = (green() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.green()) / d;
+        byte b = (blue() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.blue()) / d;
+        byte a = d / 255;
+        return Color(r, g, b, a);
+    }
 
     RGBA32 value() const { return m_value; }
 
 private:
+    explicit Color(RGBA32 rgba) : m_value(rgba) { }
+
     RGBA32 m_value { 0 };
 };

+ 14 - 11
SharedGraphics/GraphicsBitmap.cpp

@@ -5,14 +5,15 @@
 #include <errno.h>
 #include <stdio.h>
 
-RetainPtr<GraphicsBitmap> GraphicsBitmap::create(const Size& size)
+RetainPtr<GraphicsBitmap> GraphicsBitmap::create(Format format, const Size& size)
 {
-    return adopt(*new GraphicsBitmap(size));
+    return adopt(*new GraphicsBitmap(format, size));
 }
 
-GraphicsBitmap::GraphicsBitmap(const Size& size)
+GraphicsBitmap::GraphicsBitmap(Format format, const Size& size)
     : m_size(size)
     , m_pitch(size.width() * sizeof(RGBA32))
+    , m_format(format)
 {
     size_t size_in_bytes = size.area() * sizeof(RGBA32);
     m_data = (RGBA32*)mmap(nullptr, size_in_bytes, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
@@ -21,12 +22,12 @@ GraphicsBitmap::GraphicsBitmap(const Size& size)
     m_mmaped = true;
 }
 
-RetainPtr<GraphicsBitmap> GraphicsBitmap::create_wrapper(const Size& size, RGBA32* data)
+RetainPtr<GraphicsBitmap> GraphicsBitmap::create_wrapper(Format format, const Size& size, RGBA32* data)
 {
-    return adopt(*new GraphicsBitmap(size, data));
+    return adopt(*new GraphicsBitmap(format, size, data));
 }
 
-RetainPtr<GraphicsBitmap> GraphicsBitmap::load_from_file(const String& path, const Size& size)
+RetainPtr<GraphicsBitmap> GraphicsBitmap::load_from_file(Format format, const String& path, const Size& size)
 {
     int fd = open(path.characters(), O_RDONLY, 0644);
     if (fd < 0) {
@@ -44,19 +45,20 @@ RetainPtr<GraphicsBitmap> GraphicsBitmap::load_from_file(const String& path, con
 
     int rc = close(fd);
     ASSERT(rc == 0);
-    auto bitmap = create_wrapper(size, mapped_data);
+    auto bitmap = create_wrapper(format, size, mapped_data);
     bitmap->m_mmaped = true;
     return bitmap;
 }
 
-GraphicsBitmap::GraphicsBitmap(const Size& size, RGBA32* data)
+GraphicsBitmap::GraphicsBitmap(Format format, const Size& size, RGBA32* data)
     : m_size(size)
     , m_data(data)
     , m_pitch(size.width() * sizeof(RGBA32))
+    , m_format(format)
 {
 }
 
-RetainPtr<GraphicsBitmap> GraphicsBitmap::create_with_shared_buffer(int shared_buffer_id, const Size& size, RGBA32* data)
+RetainPtr<GraphicsBitmap> GraphicsBitmap::create_with_shared_buffer(Format format, int shared_buffer_id, const Size& size, RGBA32* data)
 {
     if (!data) {
         void* shared_buffer = get_shared_buffer(shared_buffer_id);
@@ -64,13 +66,14 @@ RetainPtr<GraphicsBitmap> GraphicsBitmap::create_with_shared_buffer(int shared_b
             return nullptr;
         data = (RGBA32*)shared_buffer;
     }
-    return adopt(*new GraphicsBitmap(shared_buffer_id, size, data));
+    return adopt(*new GraphicsBitmap(format, shared_buffer_id, size, data));
 }
 
-GraphicsBitmap::GraphicsBitmap(int shared_buffer_id, const Size& size, RGBA32* data)
+GraphicsBitmap::GraphicsBitmap(Format format, int shared_buffer_id, const Size& size, RGBA32* data)
     : m_size(size)
     , m_data(data)
     , m_pitch(size.width() * sizeof(RGBA32))
+    , m_format(format)
     , m_shared_buffer_id(shared_buffer_id)
 {
 }

+ 12 - 7
SharedGraphics/GraphicsBitmap.h

@@ -9,10 +9,12 @@
 
 class GraphicsBitmap : public Retainable<GraphicsBitmap> {
 public:
-    static RetainPtr<GraphicsBitmap> create(const Size&);
-    static RetainPtr<GraphicsBitmap> create_wrapper(const Size&, RGBA32*);
-    static RetainPtr<GraphicsBitmap> load_from_file(const String& path, const Size&);
-    static RetainPtr<GraphicsBitmap> create_with_shared_buffer(int shared_buffer_id, const Size&, RGBA32* buffer = nullptr);
+    enum class Format { Invalid, RGB32, RGBA32 };
+
+    static RetainPtr<GraphicsBitmap> create(Format, const Size&);
+    static RetainPtr<GraphicsBitmap> create_wrapper(Format, const Size&, RGBA32*);
+    static RetainPtr<GraphicsBitmap> load_from_file(Format, const String& path, const Size&);
+    static RetainPtr<GraphicsBitmap> create_with_shared_buffer(Format, int shared_buffer_id, const Size&, RGBA32* buffer = nullptr);
     ~GraphicsBitmap();
 
     RGBA32* scanline(int y);
@@ -25,14 +27,17 @@ public:
     size_t pitch() const { return m_pitch; }
     int shared_buffer_id() const { return m_shared_buffer_id; }
 
+    bool has_alpha_channel() const { return m_format == Format::RGBA32; }
+
 private:
-    GraphicsBitmap(const Size&);
-    GraphicsBitmap(const Size&, RGBA32*);
-    GraphicsBitmap(int shared_buffer_id, const Size&, RGBA32*);
+    GraphicsBitmap(Format, const Size&);
+    GraphicsBitmap(Format, const Size&, RGBA32*);
+    GraphicsBitmap(Format, int shared_buffer_id, const Size&, RGBA32*);
 
     Size m_size;
     RGBA32* m_data { nullptr };
     size_t m_pitch { 0 };
+    Format m_format { Format::Invalid };
     bool m_mmaped { false };
     int m_shared_buffer_id { -1 };
 };

+ 47 - 4
SharedGraphics/Painter.cpp

@@ -32,7 +32,10 @@ Painter::Painter(GWidget& widget)
     request.window_id = widget.window()->window_id();
     auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::DidGetWindowBackingStore);
 
-    m_target = GraphicsBitmap::create_with_shared_buffer(response.backing.shared_buffer_id, response.backing.size);
+    m_target = GraphicsBitmap::create_with_shared_buffer(
+                   response.backing.has_alpha_channel ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32,
+                   response.backing.shared_buffer_id,
+                   response.backing.size);
     ASSERT(m_target);
     m_window = widget.window();
     m_translation.move_by(widget.window_relative_rect().location());
@@ -67,7 +70,7 @@ void Painter::fill_rect_with_draw_op(const Rect& a_rect, Color color)
 
     for (int i = rect.height() - 1; i >= 0; --i) {
         for (int j = 0; j < rect.width(); ++j)
-            set_pixel_with_draw_op(dst[j], color.value());
+            set_pixel_with_draw_op(dst[j], color);
         dst += dst_skip;
     }
 }
@@ -230,8 +233,46 @@ void Painter::draw_bitmap(const Point& p, const GlyphBitmap& bitmap, Color color
     }
 }
 
+void Painter::blit_with_opacity(const Point& position, const GraphicsBitmap& source, const Rect& src_rect, float opacity)
+{
+    ASSERT(!m_target->has_alpha_channel());
+
+    if (!opacity)
+        return;
+    if (opacity >= 1.0f)
+        return blit(position, source, src_rect);
+
+    byte alpha = 255 * opacity;
+
+    Rect dst_rect(position, src_rect.size());
+    dst_rect.move_by(m_translation);
+    auto clipped_rect = Rect::intersection(dst_rect, m_clip_rect);
+    if (clipped_rect.is_empty())
+        return;
+    const int first_row = clipped_rect.top() - dst_rect.top();
+    const int last_row = clipped_rect.bottom() - dst_rect.top();
+    const int first_column = clipped_rect.left() - dst_rect.left();
+    const int last_column = clipped_rect.right() - dst_rect.left();
+    RGBA32* dst = m_target->scanline(clipped_rect.y()) + clipped_rect.x();
+    const RGBA32* src = source.scanline(src_rect.top() + first_row) + src_rect.left() + first_column;
+    const size_t dst_skip = m_target->width();
+    const unsigned src_skip = source.width();
+
+    for (int row = first_row; row <= last_row; ++row) {
+        for (int x = 0; x <= (last_column - first_column); ++x) {
+            Color src_color_with_alpha = Color::from_rgb(src[x]);
+            src_color_with_alpha.set_alpha(alpha);
+            Color dst_color = Color::from_rgb(dst[x]);
+            dst[x] = dst_color.blend(src_color_with_alpha).value();
+        }
+        dst += dst_skip;
+        src += src_skip;
+    }
+}
+
 void Painter::blit_with_alpha(const Point& position, const GraphicsBitmap& source, const Rect& src_rect)
 {
+    ASSERT(source.has_alpha_channel());
     Rect dst_rect(position, src_rect.size());
     dst_rect.move_by(m_translation);
     auto clipped_rect = Rect::intersection(dst_rect, m_clip_rect);
@@ -248,13 +289,13 @@ void Painter::blit_with_alpha(const Point& position, const GraphicsBitmap& sourc
 
     for (int row = first_row; row <= last_row; ++row) {
         for (int x = 0; x <= (last_column - first_column); ++x) {
-            byte alpha = Color(src[x]).alpha();
+            byte alpha = Color::from_rgba(src[x]).alpha();
             if (alpha == 0xff)
                 dst[x] = src[x];
             else if (!alpha)
                 continue;
             else
-                dst[x] = Color(dst[x]).blend(src[x]).value();
+                dst[x] = Color::from_rgba(dst[x]).blend(Color::from_rgba(src[x])).value();
         }
         dst += dst_skip;
         src += src_skip;
@@ -263,6 +304,8 @@ void Painter::blit_with_alpha(const Point& position, const GraphicsBitmap& sourc
 
 void Painter::blit(const Point& position, const GraphicsBitmap& source, const Rect& src_rect)
 {
+    if (source.has_alpha_channel())
+        return blit_with_alpha(position, source, src_rect);
     Rect dst_rect(position, src_rect.size());
     dst_rect.move_by(m_translation);
     auto clipped_rect = Rect::intersection(dst_rect, m_clip_rect);

+ 2 - 1
SharedGraphics/Painter.h

@@ -34,7 +34,7 @@ public:
     void draw_line(const Point&, const Point&, Color);
     void draw_focus_rect(const Rect&);
     void blit(const Point&, const GraphicsBitmap&, const Rect& src_rect);
-    void blit_with_alpha(const Point&, const GraphicsBitmap&, const Rect& src_rect);
+    void blit_with_opacity(const Point&, const GraphicsBitmap&, const Rect& src_rect, float opacity);
 
     void draw_text(const Rect&, const String&, TextAlignment = TextAlignment::TopLeft, Color = Color());
     void draw_glyph(const Point&, char, Color);
@@ -58,6 +58,7 @@ public:
 private:
     void set_pixel_with_draw_op(dword& pixel, const Color&);
     void fill_rect_with_draw_op(const Rect&, Color);
+    void blit_with_alpha(const Point&, const GraphicsBitmap&, const Rect& src_rect);
 
     const Font* m_font;
     Point m_translation;

+ 4 - 0
WindowServer/WSAPITypes.h

@@ -117,6 +117,7 @@ struct WSAPI_ServerMessage {
             size_t bpp;
             size_t pitch;
             int shared_buffer_id;
+            bool has_alpha_channel;
         } backing;
     };
 };
@@ -142,6 +143,7 @@ struct WSAPI_ClientMessage {
         DidFinishPainting,
         GetWindowBackingStore,
         SetGlobalCursorTracking,
+        SetWindowOpacity,
     };
     Type type { Invalid };
     int window_id { -1 };
@@ -157,6 +159,8 @@ struct WSAPI_ClientMessage {
         } menu;
         struct {
             WSAPI_Rect rect;
+            bool has_alpha_channel;
+            float opacity;
         } window;
     };
 };

+ 19 - 2
WindowServer/WSClientConnection.cpp

@@ -79,14 +79,14 @@ void WSClientConnection::post_message(const WSAPI_ServerMessage& message)
     ASSERT(nwritten == sizeof(message));
 }
 
-RetainPtr<GraphicsBitmap> WSClientConnection::create_shared_bitmap(const Size& size)
+RetainPtr<GraphicsBitmap> WSClientConnection::create_shared_bitmap(GraphicsBitmap::Format format, const Size& size)
 {
     RGBA32* buffer;
     int shared_buffer_id = create_shared_buffer(m_pid, size.area() * sizeof(RGBA32), (void**)&buffer);
     ASSERT(shared_buffer_id >= 0);
     ASSERT(buffer);
     ASSERT(buffer != (void*)-1);
-    return GraphicsBitmap::create_with_shared_buffer(shared_buffer_id, size, buffer);
+    return GraphicsBitmap::create_with_shared_buffer(format, shared_buffer_id, size, buffer);
 }
 
 void WSClientConnection::on_message(WSMessage& message)
@@ -236,6 +236,18 @@ void WSClientConnection::handle_request(WSAPIAddMenuSeparatorRequest& request)
     post_message(response);
 }
 
+void WSClientConnection::handle_request(WSAPISetWindowOpacityRequest& request)
+{
+    int window_id = request.window_id();
+    auto it = m_windows.find(window_id);
+    if (it == m_windows.end()) {
+        post_error("Bad window ID");
+        return;
+    }
+    auto& window = *(*it).value;
+    window.set_opacity(request.opacity());
+}
+
 void WSClientConnection::handle_request(WSAPISetWindowTitleRequest& request)
 {
     int window_id = request.window_id();
@@ -298,8 +310,10 @@ void WSClientConnection::handle_request(WSAPICreateWindowRequest& request)
 {
     int window_id = m_next_window_id++;
     auto window = make<WSWindow>(*this, window_id);
+    window->set_has_alpha_channel(request.has_alpha_channel());
     window->set_title(request.title());
     window->set_rect(request.rect());
+    window->set_opacity(request.opacity());
     m_windows.set(window_id, move(window));
     WSAPI_ServerMessage response;
     response.type = WSAPI_ServerMessage::Type::DidCreateWindow;
@@ -364,6 +378,7 @@ void WSClientConnection::handle_request(WSAPIGetWindowBackingStoreRequest& reque
     response.backing.bpp = sizeof(RGBA32);
     response.backing.pitch = backing_store->pitch();
     response.backing.size = backing_store->size();
+    response.backing.has_alpha_channel = backing_store->has_alpha_channel();
     response.backing.shared_buffer_id = backing_store->shared_buffer_id();
     post_message(response);
 }
@@ -419,6 +434,8 @@ void WSClientConnection::on_request(WSAPIClientRequest& request)
         return handle_request(static_cast<WSAPIGetWindowBackingStoreRequest&>(request));
     case WSMessage::APISetGlobalCursorTrackingRequest:
         return handle_request(static_cast<WSAPISetGlobalCursorTrackingRequest&>(request));
+    case WSMessage::APISetWindowOpacityRequest:
+        return handle_request(static_cast<WSAPISetWindowOpacityRequest&>(request));
     default:
         break;
     }

+ 2 - 1
WindowServer/WSClientConnection.h

@@ -22,7 +22,7 @@ public:
     static void for_each_client(Function<void(WSClientConnection&)>);
 
     void post_message(const WSAPI_ServerMessage&);
-    RetainPtr<GraphicsBitmap> create_shared_bitmap(const Size&);
+    RetainPtr<GraphicsBitmap> create_shared_bitmap(GraphicsBitmap::Format, const Size&);
 
     int client_id() const { return m_client_id; }
     WSMenuBar* app_menubar() { return m_app_menubar.ptr(); }
@@ -52,6 +52,7 @@ private:
     void handle_request(WSAPIDidFinishPaintingNotification&);
     void handle_request(WSAPIGetWindowBackingStoreRequest&);
     void handle_request(WSAPISetGlobalCursorTrackingRequest&);
+    void handle_request(WSAPISetWindowOpacityRequest&);
 
     void post_error(const String&);
 

+ 28 - 1
WindowServer/WSMessage.h

@@ -39,6 +39,7 @@ public:
         APIDidFinishPaintingNotification,
         APIGetWindowBackingStoreRequest,
         APISetGlobalCursorTrackingRequest,
+        APISetWindowOpacityRequest,
         __End_API_Client_Requests,
     };
 
@@ -253,6 +254,26 @@ private:
     int m_window_id { 0 };
 };
 
+class WSAPISetWindowOpacityRequest final : public WSAPIClientRequest {
+public:
+    explicit WSAPISetWindowOpacityRequest(int client_id, int window_id, float opacity)
+        : WSAPIClientRequest(WSMessage::APISetWindowOpacityRequest, client_id)
+        , m_client_id(client_id)
+        , m_window_id(window_id)
+        , m_opacity(opacity)
+    {
+    }
+
+    int client_id() const { return m_client_id; }
+    int window_id() const { return m_window_id; }
+    float opacity() const { return m_opacity; }
+
+private:
+    int m_client_id { 0 };
+    int m_window_id { 0 };
+    float m_opacity { 0 };
+};
+
 class WSAPISetWindowRectRequest final : public WSAPIClientRequest {
 public:
     explicit WSAPISetWindowRectRequest(int client_id, int window_id, const Rect& rect)
@@ -292,19 +313,25 @@ private:
 
 class WSAPICreateWindowRequest : public WSAPIClientRequest {
 public:
-    WSAPICreateWindowRequest(int client_id, const Rect& rect, const String& title)
+    WSAPICreateWindowRequest(int client_id, const Rect& rect, const String& title, bool has_alpha_channel, float opacity)
         : WSAPIClientRequest(WSMessage::APICreateWindowRequest, client_id)
         , m_rect(rect)
         , m_title(title)
+        , m_opacity(opacity)
+        , m_has_alpha_channel(has_alpha_channel)
     {
     }
 
     Rect rect() const { return m_rect; }
     String title() const { return m_title; }
+    bool has_alpha_channel() const { return m_has_alpha_channel; }
+    float opacity() const { return m_opacity; }
 
 private:
     Rect m_rect;
     String m_title;
+    float m_opacity { 0 };
+    bool m_has_alpha_channel { false };
 };
 
 class WSAPIDestroyWindowRequest : public WSAPIClientRequest {

+ 1 - 1
WindowServer/WSMessageLoop.cpp

@@ -302,7 +302,7 @@ void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMess
         break;
     case WSAPI_ClientMessage::Type::CreateWindow:
         ASSERT(message.text_length < sizeof(message.text));
-        post_message(client, make<WSAPICreateWindowRequest>(client_id, message.window.rect, String(message.text, message.text_length)));
+        post_message(client, make<WSAPICreateWindowRequest>(client_id, message.window.rect, String(message.text, message.text_length), message.window.has_alpha_channel, message.window.opacity));
         break;
     case WSAPI_ClientMessage::Type::DestroyWindow:
         post_message(client, make<WSAPIDestroyWindowRequest>(client_id, message.window_id));

+ 4 - 3
WindowServer/WSWindow.cpp

@@ -44,9 +44,10 @@ void WSWindow::set_rect(const Rect& rect)
     m_rect = rect;
     if (!m_backing || old_rect.size() != rect.size()) {
         if (m_menu)
-            m_backing = GraphicsBitmap::create(m_rect.size());
-        else if (m_client)
-            m_backing = m_client->create_shared_bitmap(m_rect.size());
+            m_backing = GraphicsBitmap::create(GraphicsBitmap::Format::RGB32, m_rect.size());
+        else if (m_client) {
+            m_backing = m_client->create_shared_bitmap(m_has_alpha_channel ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32, m_rect.size());
+        }
 
     }
     WSWindowManager::the().notify_rect_changed(*this, old_rect, rect);

+ 8 - 2
WindowServer/WSWindow.h

@@ -25,6 +25,9 @@ public:
     String title() const { return m_title; }
     void set_title(String&&);
 
+    float opacity() const { return m_opacity; }
+    void set_opacity(float opacity) { m_opacity = opacity; }
+
     int x() const { return m_rect.x(); }
     int y() const { return m_rect.y(); }
     int width() const { return m_rect.width(); }
@@ -59,6 +62,9 @@ public:
     void set_global_cursor_tracking_enabled(bool);
     bool global_cursor_tracking() const { return m_global_cursor_tracking_enabled; }
 
+    bool has_alpha_channel() const { return m_has_alpha_channel; }
+    void set_has_alpha_channel(bool value) { m_has_alpha_channel = value; }
+
     // For InlineLinkedList.
     // FIXME: Maybe make a ListHashSet and then WSWindowManager can just use that.
     WSWindow* m_next { nullptr };
@@ -72,9 +78,9 @@ private:
     bool m_is_being_dragged { false };
     bool m_global_cursor_tracking_enabled { false };
     bool m_visible { true };
-
+    bool m_has_alpha_channel { false };
     WSMenu* m_menu { nullptr };
-
     RetainPtr<GraphicsBitmap> m_backing;
     int m_window_id { -1 };
+    float m_opacity { 1 };
 };

+ 20 - 11
WindowServer/WSWindowManager.cpp

@@ -144,8 +144,8 @@ WSWindowManager::WSWindowManager()
     (void)m_flush_count;
 #endif
     auto size = m_screen_rect.size();
-    m_front_bitmap = GraphicsBitmap::create_wrapper(size, m_screen.scanline(0));
-    m_back_bitmap = GraphicsBitmap::create_wrapper(size, m_screen.scanline(size.height()));
+    m_front_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, m_screen.scanline(0));
+    m_back_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, m_screen.scanline(size.height()));
 
     m_front_painter = make<Painter>(*m_front_bitmap);
     m_back_painter = make<Painter>(*m_back_bitmap);
@@ -170,17 +170,17 @@ WSWindowManager::WSWindowManager()
     m_cursor_bitmap_outer = CharacterBitmap::create_from_ascii(cursor_bitmap_outer_ascii, 12, 17);
 
     m_wallpaper_path = "/res/wallpapers/cool.rgb";
-    m_wallpaper = GraphicsBitmap::load_from_file(m_wallpaper_path, { 1024, 768 });
+    m_wallpaper = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, m_wallpaper_path, { 1024, 768 });
 
 #ifdef KERNEL
     ProcFS::the().add_sys_bool("wm_flash_flush", m_flash_flush);
     ProcFS::the().add_sys_string("wm_wallpaper", m_wallpaper_path, [this] {
-        m_wallpaper = GraphicsBitmap::load_from_file(m_wallpaper_path, m_screen_rect.size());
+        m_wallpaper = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, m_wallpaper_path, m_screen_rect.size());
         invalidate(m_screen_rect);
     });
 #endif
 
-    m_menu_selection_color = Color(0x84351a);
+    m_menu_selection_color = Color::from_rgb(0x84351a);
 
     {
         byte system_menu_name[] = { 0xf8, 0 };
@@ -242,8 +242,8 @@ void WSWindowManager::set_resolution(int width, int height)
         return;
     m_screen.set_resolution(width, height);
     m_screen_rect = m_screen.rect();
-    m_front_bitmap = GraphicsBitmap::create_wrapper({ width, height }, m_screen.scanline(0));
-    m_back_bitmap = GraphicsBitmap::create_wrapper({ width, height }, m_screen.scanline(height));
+    m_front_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, { width, height }, m_screen.scanline(0));
+    m_back_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, { width, height }, m_screen.scanline(height));
     m_front_painter = make<Painter>(*m_front_bitmap);
     m_back_painter = make<Painter>(*m_back_bitmap);
     m_buffers_are_flipped = false;
@@ -620,10 +620,17 @@ void WSWindowManager::compose()
     dbgprintf("[WM] compose #%u (%u rects)\n", ++m_compose_count, dirty_rects.size());
 #endif
 
-    auto any_window_contains_rect = [this] (const Rect& r) {
+    auto any_opaque_window_contains_rect = [this] (const Rect& r) {
         for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
             if (!window->is_visible())
                 continue;
+            if (window->opacity() < 1.0f)
+                continue;
+            if (window->has_alpha_channel()) {
+                // FIXME: Just because the window has an alpha channel doesn't mean it's not opaque.
+                //        Maybe there's some way we could know this?
+                continue;
+            }
             if (outer_window_rect(window->rect()).contains(r))
                 return true;
         }
@@ -640,9 +647,8 @@ void WSWindowManager::compose()
     };
 
     for (auto& dirty_rect : dirty_rects) {
-        if (any_window_contains_rect(dirty_rect)) {
+        if (any_opaque_window_contains_rect(dirty_rect))
             continue;
-        }
         if (!m_wallpaper)
             m_back_painter->fill_rect(dirty_rect, m_background_color);
         else
@@ -665,7 +671,10 @@ void WSWindowManager::compose()
             dirty_rect_in_window_coordinates.set_y(dirty_rect_in_window_coordinates.y() - window.y());
             auto dst = window.position();
             dst.move_by(dirty_rect_in_window_coordinates.location());
-            m_back_painter->blit(dst, *backing, dirty_rect_in_window_coordinates);
+            if (window.opacity() == 1.0f)
+                m_back_painter->blit(dst, *backing, dirty_rect_in_window_coordinates);
+            else
+                m_back_painter->blit_with_opacity(dst, *backing, dirty_rect_in_window_coordinates, window.opacity());
             m_back_painter->clear_clip_rect();
         }
         m_back_painter->clear_clip_rect();