mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 05:47:35 +00:00
WindowServer: Cache rendered window frame in bitmap
This only renders the window frame once until the size of the window changes, or some other event requires re-rendering. It is rendered to a temporary bitmap, and then the top and bottom part is stored in one bitmap as well as the left and right part. This also adds an opacity setting, allowing it to be rendered with a different opacity. This makes it easier to enhance window themes and allows using arbitrary bitmaps with e.g. alpha channels for e.g. shadows.
This commit is contained in:
parent
8ff34f96b6
commit
15a1d9aa94
4 changed files with 138 additions and 10 deletions
|
@ -33,6 +33,7 @@
|
|||
#include <WindowServer/Button.h>
|
||||
#include <WindowServer/Compositor.h>
|
||||
#include <WindowServer/Event.h>
|
||||
#include <WindowServer/Screen.h>
|
||||
#include <WindowServer/Window.h>
|
||||
#include <WindowServer/WindowFrame.h>
|
||||
#include <WindowServer/WindowManager.h>
|
||||
|
@ -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<Gfx::Bitmap> 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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue