diff --git a/Userland/Services/WindowServer/Compositor.cpp b/Userland/Services/WindowServer/Compositor.cpp index b0353a496a..55e4f6a2ae 100644 --- a/Userland/Services/WindowServer/Compositor.cpp +++ b/Userland/Services/WindowServer/Compositor.cpp @@ -288,11 +288,10 @@ void Compositor::compose() auto compose_window_rect = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) { if (!window.is_fullscreen()) { rect.for_each_intersected(frame_rects, [&](const Gfx::IntRect& intersected_rect) { - // TODO: Should optimize this to use a backing buffer Gfx::PainterStateSaver saver(painter); painter.add_clip_rect(intersected_rect); dbgln_if(COMPOSE_DEBUG, " render frame: {}", intersected_rect); - window.frame().paint(painter); + window.frame().paint(painter, intersected_rect); return IterationDecision::Continue; }); } diff --git a/Userland/Services/WindowServer/WindowFrame.cpp b/Userland/Services/WindowServer/WindowFrame.cpp index c4e4dd0f2a..77b1863ffc 100644 --- a/Userland/Services/WindowServer/WindowFrame.cpp +++ b/Userland/Services/WindowServer/WindowFrame.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -200,14 +201,54 @@ void WindowFrame::paint_normal_frame(Gfx::Painter& painter) Gfx::WindowTheme::current().paint_normal_frame(painter, window_state_for_theme(), m_window.rect(), title_text, m_window.icon(), palette, leftmost_button_rect); } -void WindowFrame::paint(Gfx::Painter& painter) +void WindowFrame::paint(Gfx::Painter& painter, const Gfx::IntRect& rect) +{ + render_to_cache(); + + auto frame_rect = this->rect(); + auto window_rect = m_window.rect(); + + if (m_top_bottom) { + auto top_bottom_height = frame_rect.height() - window_rect.height(); + if (m_bottom_y > 0) { + // We have a top piece + auto src_rect = rect.intersected({ frame_rect.location(), { frame_rect.width(), m_bottom_y } }); + if (!src_rect.is_empty()) + painter.blit(src_rect.location(), *m_top_bottom, src_rect.translated(-frame_rect.location()), m_opacity); + } + if (m_bottom_y < top_bottom_height) { + // We have a bottom piece + Gfx::IntRect rect_in_frame { frame_rect.x(), window_rect.bottom() + 1, frame_rect.width(), top_bottom_height - m_bottom_y }; + auto src_rect = rect.intersected(rect_in_frame); + if (!src_rect.is_empty()) + painter.blit(src_rect.location(), *m_top_bottom, src_rect.translated(-rect_in_frame.x(), -rect_in_frame.y() + m_bottom_y), m_opacity); + } + } + + if (m_left_right) { + auto left_right_width = frame_rect.width() - window_rect.width(); + if (m_right_x > 0) { + // We have a left piece + Gfx::IntRect rect_in_frame { frame_rect.x(), window_rect.y(), m_right_x, window_rect.height() }; + auto src_rect = rect.intersected(rect_in_frame); + if (!src_rect.is_empty()) + painter.blit(src_rect.location(), *m_left_right, src_rect.translated(-rect_in_frame.location()), m_opacity); + } + if (m_right_x < left_right_width) { + // We have a right piece + Gfx::IntRect rect_in_frame { window_rect.right() + 1, window_rect.y(), left_right_width - m_right_x, window_rect.height() }; + auto src_rect = rect.intersected(rect_in_frame); + if (!src_rect.is_empty()) + painter.blit(src_rect.location(), *m_left_right, src_rect.translated(-rect_in_frame.x() + m_right_x, -rect_in_frame.y()), m_opacity); + } + } +} + +void WindowFrame::render(Gfx::Painter& painter) { if (m_window.is_frameless()) return; - Gfx::PainterStateSaver saver(painter); - painter.translate(rect().location()); - if (m_window.type() == WindowType::Notification) paint_notification_frame(painter); else if (m_window.type() == WindowType::Normal) @@ -220,6 +261,70 @@ void WindowFrame::paint(Gfx::Painter& painter) } } +void WindowFrame::render_to_cache() +{ + if (!m_dirty) + return; + m_dirty = true; + + static RefPtr s_tmp_bitmap; + auto frame_rect = rect(); + auto window_rect = m_window.rect(); + auto scale = Screen::the().scale_factor(); + if (!s_tmp_bitmap || !s_tmp_bitmap->size().contains(frame_rect.size()) || s_tmp_bitmap->scale() != scale) + s_tmp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, frame_rect.size(), scale); + + Gfx::Painter painter(*s_tmp_bitmap); + painter.fill_rect({ { 0, 0 }, frame_rect.size() }, Color::White); + render(painter); + + auto top_bottom_height = frame_rect.height() - window_rect.height(); + if (top_bottom_height > 0) { + if (!m_top_bottom || m_top_bottom->width() != frame_rect.width() || m_top_bottom->height() != top_bottom_height || m_top_bottom->scale() != scale) + m_top_bottom = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { frame_rect.width(), top_bottom_height }, scale); + m_bottom_y = window_rect.y() - frame_rect.y(); + ASSERT(m_bottom_y >= 0); + + Gfx::Painter top_bottom_painter(*m_top_bottom); + if (m_bottom_y > 0) + top_bottom_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, 0, frame_rect.width(), m_bottom_y }); + if (m_bottom_y < top_bottom_height) + top_bottom_painter.blit({ 0, m_bottom_y }, *s_tmp_bitmap, { 0, frame_rect.height() - (frame_rect.bottom() - window_rect.bottom()), frame_rect.width(), top_bottom_height - m_bottom_y }); + } else { + m_top_bottom = nullptr; + m_bottom_y = 0; + } + + auto left_right_width = frame_rect.width() - window_rect.width(); + if (left_right_width > 0) { + if (!m_left_right || m_left_right->height() != frame_rect.height() || m_left_right->width() != left_right_width || m_left_right->scale() != scale) + m_left_right = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { left_right_width, frame_rect.height() }, scale); + m_right_x = window_rect.x() - frame_rect.x(); + ASSERT(m_right_x >= 0); + + Gfx::Painter left_right_painter(*m_left_right); + if (m_right_x > 0) + left_right_painter.blit({ 0, 0 }, *s_tmp_bitmap, { 0, m_bottom_y, m_right_x, window_rect.height() }); + if (m_right_x < left_right_width) + left_right_painter.blit({ m_right_x, 0 }, *s_tmp_bitmap, { (window_rect.right() - frame_rect.x()) + 1, m_bottom_y, frame_rect.width() - (frame_rect.right() - window_rect.right()), window_rect.height() }); + } else { + m_left_right = nullptr; + m_right_x = 0; + } +} + +void WindowFrame::set_opacity(float opacity) +{ + if (m_opacity == opacity) + return; + bool was_opaque = is_opaque(); + m_opacity = opacity; + if (was_opaque != is_opaque()) + Compositor::the().invalidate_occlusions(); + Compositor::the().invalidate_screen(rect()); + WindowManager::the().notify_opacity_changed(m_window); +} + static Gfx::IntRect frame_rect_for_window(Window& window, const Gfx::IntRect& rect) { if (window.is_frameless()) @@ -239,6 +344,7 @@ Gfx::IntRect WindowFrame::rect() const void WindowFrame::invalidate_title_bar() { + m_dirty = true; invalidate(title_bar_rect()); } @@ -255,6 +361,9 @@ void WindowFrame::notify_window_rect_changed(const Gfx::IntRect& old_rect, const layout_buttons(); auto old_frame_rect = frame_rect_for_window(m_window, old_rect); + auto new_frame_rect = frame_rect_for_window(m_window, new_rect); + if (old_frame_rect.width() != new_frame_rect.width()) + m_dirty = true; auto& compositor = Compositor::the(); for (auto& dirty : old_frame_rect.shatter(rect())) compositor.invalidate_screen(dirty); diff --git a/Userland/Services/WindowServer/WindowFrame.h b/Userland/Services/WindowServer/WindowFrame.h index 26d2f6621d..808bc1be82 100644 --- a/Userland/Services/WindowServer/WindowFrame.h +++ b/Userland/Services/WindowServer/WindowFrame.h @@ -45,7 +45,9 @@ public: ~WindowFrame(); Gfx::IntRect rect() const; - void paint(Gfx::Painter&); + void paint(Gfx::Painter&, const Gfx::IntRect&); + void render(Gfx::Painter&); + void render_to_cache(); void on_mouse_event(const MouseEvent&); void notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect); void invalidate_title_bar(); @@ -62,17 +64,26 @@ public: void start_flash_animation(); + bool has_alpha_channel() const { return m_has_alpha_channel; } + void set_has_alpha_channel(bool value) { m_has_alpha_channel = value; } + + void set_opacity(float); float opacity() const { return m_opacity; } bool is_opaque() const { if (opacity() < 1.0f) return false; - //if (has_alpha_channel()) - // return false; + if (has_alpha_channel()) + return false; return true; } + void scale_changed() + { + m_dirty = true; + } + private: void paint_notification_frame(Gfx::Painter&); void paint_normal_frame(Gfx::Painter&); @@ -85,9 +96,16 @@ private: Button* m_maximize_button { nullptr }; Button* m_minimize_button { nullptr }; + RefPtr m_top_bottom; + RefPtr m_left_right; + int m_bottom_y { 0 }; // y-offset in m_top_bottom for the bottom half + int m_right_x { 0 }; // x-offset in m_left_right for the right half + RefPtr m_flash_timer; size_t m_flash_counter { 0 }; float m_opacity { 1 }; + bool m_has_alpha_channel { false }; + bool m_dirty { false }; }; } diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp index 3ce041bc83..2c52c695c0 100644 --- a/Userland/Services/WindowServer/WindowManager.cpp +++ b/Userland/Services/WindowServer/WindowManager.cpp @@ -1515,7 +1515,9 @@ void WindowManager::reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icon m_allow_hidpi_icons = allow_hidpi_icons; reload_config(); for_each_window([&](Window& window) { - window.frame().set_button_icons(); + auto& window_frame = window.frame(); + window_frame.set_button_icons(); + window_frame.scale_changed(); return IterationDecision::Continue; }); }