diff --git a/repos/demo/src/server/liquid_framebuffer/services.cc b/repos/demo/src/server/liquid_framebuffer/services.cc index 0df745cb7d..ff95c10d4f 100644 --- a/repos/demo/src/server/liquid_framebuffer/services.cc +++ b/repos/demo/src/server/liquid_framebuffer/services.cc @@ -266,6 +266,17 @@ class Framebuffer::Session_component : public Genode::Rpc_object { _window_content.redraw_area(rect.x1(), rect.y1(), rect.w(), rect.h()); } + + Blit_result blit(Blit_batch const &) override + { + warning("Framebuffer::Session::blit not supported"); + return Blit_result::OK; + } + + void panning(Point) override + { + warning("Framebuffer::Session::panning not supported"); + } }; diff --git a/repos/gems/src/server/gui_fader/main.cc b/repos/gems/src/server/gui_fader/main.cc index f19112bf5a..07499a89e8 100644 --- a/repos/gems/src/server/gui_fader/main.cc +++ b/repos/gems/src/server/gui_fader/main.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,7 @@ class Gui_fader::Src_buffer bool const _use_alpha; Attached_ram_dataspace _ds; Texture _texture; + bool _warned_once = false; static size_t _needed_bytes(Area size) { @@ -74,6 +76,12 @@ class Gui_fader::Src_buffer return size.count() * (1 + 1 + sizeof(Pixel)); } + void _with_pixel_surface(auto const &fn) + { + Surface pixel { _ds.local_addr(), _texture.size() }; + fn(pixel); + } + public: /** @@ -93,6 +101,18 @@ class Gui_fader::Src_buffer Texture const &texture() const { return _texture; } bool use_alpha() const { return _use_alpha; } + + void blit(Rect from, Point to) + { + if (_use_alpha && !_warned_once) { + Genode::warning("Framebuffer::Session::blit does not support alpha blending"); + _warned_once = true; + } + + _with_pixel_surface([&] (Surface &surface) { + surface.clip({ to, from.area }); + Blit_painter::paint(surface, _texture, to - from.p1()); }); + } }; @@ -142,7 +162,6 @@ class Gui_fader::Framebuffer_session_component Constructible _dst_buffer { }; - Lazy_value _fade { }; public: @@ -246,6 +265,29 @@ class Gui_fader::Framebuffer_session_component _gui.framebuffer.refresh(rect); } + Blit_result blit(Framebuffer::Blit_batch const &batch) override + { + Framebuffer::Mode const mode { .area = _src_buffer.texture().size() }; + for (Framebuffer::Transfer const &transfer : batch.transfer) { + if (transfer.valid(mode)) { + _src_buffer.blit(transfer.from, transfer.to); + Rect const to_rect { transfer.to, transfer.from.area }; + refresh(to_rect); + } + } + return Blit_result::OK; + } + + void panning(Point pos) override + { + Rect const rect { { }, _src_buffer.texture().size() }; + + transfer_src_to_dst_pixel(rect); + transfer_src_to_dst_alpha(rect); + + _gui.framebuffer.panning(pos); + } + void sync_sigh(Genode::Signal_context_capability sigh) override { _gui.framebuffer.sync_sigh(sigh); diff --git a/repos/os/include/framebuffer_session/client.h b/repos/os/include/framebuffer_session/client.h index 7c38ada337..0e563fb774 100644 --- a/repos/os/include/framebuffer_session/client.h +++ b/repos/os/include/framebuffer_session/client.h @@ -22,19 +22,15 @@ namespace Framebuffer { struct Session_client; } struct Framebuffer::Session_client : Rpc_client { - explicit Session_client(Session_capability session) - : Rpc_client(session) { } + explicit Session_client(Session_capability cap) : Rpc_client(cap) { } - Dataspace_capability dataspace() override { - return call(); } + Dataspace_capability dataspace() override { return call(); } Mode mode() const override { return call(); } - void mode_sigh(Signal_context_capability sigh) override { - call(sigh); } + void mode_sigh(Signal_context_capability sigh) override { call(sigh); } - void sync_sigh(Signal_context_capability sigh) override { - call(sigh); } + void sync_sigh(Signal_context_capability sigh) override { call(sigh); } void refresh(Rect rect) override { call(rect); } @@ -49,6 +45,19 @@ struct Framebuffer::Session_client : Rpc_client refresh(Rect { { x, y }, { unsigned(w), unsigned(h) } }); } + Blit_result blit(Blit_batch const &batch) override { return call(batch); } + + /** + * Transfer a single pixel region within the framebuffer + */ + Blit_result blit(Rect from, Point to) + { + Blit_batch batch { }; + batch.transfer[0] = { from, to }; + return blit(batch); + } + + void panning(Point pos) override { call(pos); } }; #endif /* _INCLUDE__FRAMEBUFFER_SESSION__CLIENT_H_ */ diff --git a/repos/os/include/framebuffer_session/framebuffer_session.h b/repos/os/include/framebuffer_session/framebuffer_session.h index 6b12d5a572..cc979db90e 100644 --- a/repos/os/include/framebuffer_session/framebuffer_session.h +++ b/repos/os/include/framebuffer_session/framebuffer_session.h @@ -22,7 +22,6 @@ namespace Framebuffer { - struct Mode; struct Session; struct Session_client; @@ -31,22 +30,49 @@ namespace Framebuffer { using Area = Surface_base::Area; using Point = Surface_base::Point; using Rect = Surface_base::Rect; + + struct Mode + { + Area area; + + size_t bytes_per_pixel() const { return 4; } + + void print(Output &out) const { Genode::print(out, area); } + }; + + struct Transfer + { + Rect from; /* source rectangle */ + Point to; /* destination position */ + + /** + * Return true if transfer is applicable to 'mode' + * + * Pixels are transferred only if the source rectangle lies within + * the bounds of the framebuffer, and source does not overlap the + * destination. + */ + bool valid(Mode const &mode) const + { + Rect const fb { { }, mode.area }; + Rect const dest { to, from.area }; + + return from.area.valid() + && fb.contains(from.p1()) && fb.contains(from.p2()) + && fb.contains(dest.p1()) && fb.contains(dest.p2()) + && !Rect::intersect(from, dest).valid(); + } + }; + + struct Blit_batch + { + static constexpr unsigned N = 4; + + Transfer transfer[N]; + }; } -/** - * Framebuffer mode info as returned by 'Framebuffer::Session::mode()' - */ -struct Framebuffer::Mode -{ - Area area; - - size_t bytes_per_pixel() const { return 4; } - - void print(Output &out) const { Genode::print(out, area); } -}; - - struct Framebuffer::Session : Genode::Session { /** @@ -102,6 +128,23 @@ struct Framebuffer::Session : Genode::Session */ virtual void refresh(Rect rect) = 0; + enum class Blit_result { OK, OVERLOADED }; + + /** + * Transfer pixel regions within the framebuffer + */ + virtual Blit_result blit(Blit_batch const &) = 0; + + /** + * Define panning position of the framebuffer + * + * The panning position is the point within the framebuffer that + * corresponds to the top-left corner of the output. It is designated + * for implementing buffer flipping of double-buffered output, and for + * scrolling. + */ + virtual void panning(Point pos) = 0; + /** * Register signal handler for refresh synchronization */ @@ -115,11 +158,13 @@ struct Framebuffer::Session : Genode::Session GENODE_RPC(Rpc_dataspace, Dataspace_capability, dataspace); GENODE_RPC(Rpc_mode, Mode, mode); GENODE_RPC(Rpc_refresh, void, refresh, Rect); + GENODE_RPC(Rpc_blit, Blit_result, blit, Blit_batch const &); + GENODE_RPC(Rpc_panning, void, panning, Point); GENODE_RPC(Rpc_mode_sigh, void, mode_sigh, Signal_context_capability); GENODE_RPC(Rpc_sync_sigh, void, sync_sigh, Signal_context_capability); GENODE_RPC_INTERFACE(Rpc_dataspace, Rpc_mode, Rpc_mode_sigh, Rpc_refresh, - Rpc_sync_sigh); + Rpc_blit, Rpc_panning, Rpc_sync_sigh); }; #endif /* _INCLUDE__FRAMEBUFFER_SESSION__FRAMEBUFFER_SESSION_H_ */ diff --git a/repos/os/src/server/gui_fb/main.cc b/repos/os/src/server/gui_fb/main.cc index cd21856247..880af4af71 100644 --- a/repos/os/src/server/gui_fb/main.cc +++ b/repos/os/src/server/gui_fb/main.cc @@ -131,6 +131,13 @@ struct Framebuffer::Session_component : Genode::Rpc_object return used + usable > needed + preserved; } + void _update_view() + { + if (_dataspace_is_new) { + _view_updater.update_view(); + _dataspace_is_new = false; + } + } /** * Constructor @@ -203,14 +210,22 @@ struct Framebuffer::Session_component : Genode::Rpc_object void refresh(Rect rect) override { - if (_dataspace_is_new) { - _view_updater.update_view(); - _dataspace_is_new = false; - } - + _update_view(); _gui.framebuffer.refresh(rect); } + Blit_result blit(Blit_batch const &batch) override + { + _update_view(); + return _gui.framebuffer.blit(batch); + } + + void panning(Point pos) override + { + _update_view(); + _gui.framebuffer.panning(pos); + } + void sync_sigh(Signal_context_capability sigh) override { /* diff --git a/repos/os/src/server/nitpicker/buffer.h b/repos/os/src/server/nitpicker/buffer.h index e6c0d4bf33..dfd89f28b6 100644 --- a/repos/os/src/server/nitpicker/buffer.h +++ b/repos/os/src/server/nitpicker/buffer.h @@ -65,7 +65,11 @@ namespace Nitpicker { struct Buffer_provider; } */ struct Nitpicker::Buffer_provider : Interface { - virtual Dataspace_capability realloc_buffer(Framebuffer::Mode mode, bool use_alpha) = 0; + virtual Dataspace_capability realloc_buffer(Framebuffer::Mode, bool use_alpha) = 0; + + virtual void blit(Rect from, Point to) = 0; + + virtual void panning(Point) = 0; }; #endif /* _BUFFER_H_ */ diff --git a/repos/os/src/server/nitpicker/chunky_texture.h b/repos/os/src/server/nitpicker/chunky_texture.h index 6b39d4c274..8d0ec42fa4 100644 --- a/repos/os/src/server/nitpicker/chunky_texture.h +++ b/repos/os/src/server/nitpicker/chunky_texture.h @@ -14,6 +14,11 @@ #ifndef _CHUNKY_TEXTURE_H_ #define _CHUNKY_TEXTURE_H_ +/* Genode includes */ +#include +#include +#include + /* local includes */ #include @@ -36,6 +41,64 @@ class Nitpicker::Chunky_texture : public Buffer, public Texture return (unsigned char *)local_addr() + calc_num_bytes(size, false); } + Area _area() const { return Texture::size(); } + + void _with_pixel_surface(auto const &fn) + { + Surface pixel { (Pixel_rgb888 *)local_addr(), _area() }; + fn(pixel); + } + + static void _with_alpha_ptr(auto &obj, auto const &fn) + { + Pixel_alpha8 * const ptr = (Pixel_alpha8 *)(obj.Texture::alpha()); + if (ptr) + fn(ptr); + } + + void _with_alpha_surface(auto const &fn) + { + _with_alpha_ptr(*this, [&] (Pixel_alpha8 * const ptr) { + Surface alpha { ptr, _area() }; + fn(alpha); }); + } + + void _with_alpha_texture(auto const &fn) const + { + _with_alpha_ptr(*this, [&] (Pixel_alpha8 * const ptr) { + Texture texture { ptr, nullptr, _area() }; + fn(texture); }); + } + + static void _with_input_ptr(auto &obj, auto const &fn) + { + Pixel_alpha8 * const ptr = (Pixel_alpha8 *)(obj.input_mask_buffer()); + if (ptr) + fn(ptr); + } + + void _with_input_surface(auto const &fn) + { + _with_input_ptr(*this, [&] (Pixel_alpha8 * const ptr) { + Surface input { ptr, _area() }; + fn(input); }); + } + + void _with_input_texture(auto const &fn) const + { + _with_input_ptr(*this, [&] (Pixel_alpha8 * const ptr) { + Texture texture { ptr, nullptr, _area() }; + fn(texture); }); + } + + template + void _blit_channel(Surface &surface, Texture const &texture, + Rect const from, Point const to) + { + surface.clip({ to, from.area }); + Blit_painter::paint(surface, texture, to - from.p1()); + } + public: /** @@ -69,6 +132,20 @@ class Nitpicker::Chunky_texture : public Buffer, public Texture return (unsigned char const *)local_addr() + calc_num_bytes(size, false) + size.count(); } + + void blit(Rect from, Point to) + { + _with_pixel_surface([&] (Surface &surface) { + _blit_channel(surface, *this, from, to); }); + + _with_alpha_surface([&] (Surface &surface) { + _with_alpha_texture([&] (Texture &texture) { + _blit_channel(surface, texture, from, to); }); }); + + _with_input_surface([&] (Surface &surface) { + _with_input_texture([&] (Texture &texture) { + _blit_channel(surface, texture, from, to); }); }); + } }; #endif /* _CHUNKY_TEXTURE_H_ */ diff --git a/repos/os/src/server/nitpicker/framebuffer_session.h b/repos/os/src/server/nitpicker/framebuffer_session.h index b1eb7dd409..1d0656e801 100644 --- a/repos/os/src/server/nitpicker/framebuffer_session.h +++ b/repos/os/src/server/nitpicker/framebuffer_session.h @@ -116,6 +116,10 @@ class Framebuffer::Session_component : public Rpc_object } void refresh(Rect) override; + + Blit_result blit(Blit_batch const &) override; + + void panning(Point) override; }; #endif /* _FRAMEBUFFER_SESSION_COMPONENT_H_ */ diff --git a/repos/os/src/server/nitpicker/gui_session.h b/repos/os/src/server/nitpicker/gui_session.h index 03dc0c9f7a..66b90e8e47 100644 --- a/repos/os/src/server/nitpicker/gui_session.h +++ b/repos/os/src/server/nitpicker/gui_session.h @@ -20,8 +20,6 @@ #include #include #include -#include -#include #include /* local includes */ @@ -292,6 +290,7 @@ class Nitpicker::Gui_session : public Session_object, bool origin_pointer() const override { return _domain && _domain->origin_pointer(); } + /** * Return input mask value at specified buffer position */ @@ -375,6 +374,8 @@ class Nitpicker::Gui_session : public Session_object, _forwarded_focus = nullptr; } + Point panning() const { return _texture.panning; } + /*************************** ** GUI session interface ** @@ -416,6 +417,10 @@ class Nitpicker::Gui_session : public Session_object, *******************************/ Dataspace_capability realloc_buffer(Framebuffer::Mode mode, bool use_alpha) override; + + void blit(Rect from, Point to) override { _texture.blit(from, to); } + + void panning(Point pos) override { _texture.panning = pos; } }; #endif /* _GUI_SESSION_H_ */ diff --git a/repos/os/src/server/nitpicker/main.cc b/repos/os/src/server/nitpicker/main.cc index f3ee93956e..2cfc043b68 100644 --- a/repos/os/src/server/nitpicker/main.cc +++ b/repos/os/src/server/nitpicker/main.cc @@ -60,6 +60,27 @@ void Framebuffer::Session_component::refresh(Rect rect) } +Framebuffer::Session::Blit_result +Framebuffer::Session_component::blit(Blit_batch const &batch) +{ + for (Transfer const &transfer : batch.transfer) { + if (transfer.valid(_mode)) { + _buffer_provider.blit(transfer.from, transfer.to); + Rect const to_rect { transfer.to, transfer.from.area }; + _view_stack.mark_session_views_as_dirty(_session, to_rect); + } + } + return Blit_result::OK; +} + + +void Framebuffer::Session_component::panning(Point pos) +{ + _buffer_provider.panning(pos); + _view_stack.mark_session_views_as_dirty(_session, { { 0, 0 }, _mode.area }); +} + + /*************************************** ** Implementation of the GUI service ** ***************************************/ diff --git a/repos/os/src/server/nitpicker/resizeable_texture.h b/repos/os/src/server/nitpicker/resizeable_texture.h index f5b7436cbf..6a9a3ebdc8 100644 --- a/repos/os/src/server/nitpicker/resizeable_texture.h +++ b/repos/os/src/server/nitpicker/resizeable_texture.h @@ -14,10 +14,6 @@ #ifndef _RESIZEABLE_TEXTURE_H_ #define _RESIZEABLE_TEXTURE_H_ -/* Genode includes */ -#include -#include - /* local includes */ #include @@ -31,17 +27,18 @@ class Nitpicker::Resizeable_texture unsigned _current = 0; - using Constructible_texture = Constructible>; + Constructible> _textures[2] { }; - struct Element : Constructible_texture + static void _with_texture(auto &obj, auto const &fn) { - Element() : Constructible_texture() { } - }; - - Element _textures[2]; + if (obj.valid()) + fn(*obj._textures[obj._current]); + } public: + Point panning { 0, 0 }; + bool valid() const { return _textures[_current].constructed(); } Area size() const @@ -102,12 +99,8 @@ class Nitpicker::Resizeable_texture _current = next; } - template - void with_texture(FN const &fn) const - { - if (valid()) - fn(*_textures[_current]); - } + void with_texture(auto const &fn) const { _with_texture(*this, fn); } + void with_texture(auto const &fn) { _with_texture(*this, fn); } Dataspace_capability dataspace() { @@ -119,6 +112,12 @@ class Nitpicker::Resizeable_texture return valid() ? _textures[_current]->input_mask_buffer() : nullptr; } + + void blit(Rect from, Point to) + { + with_texture([&] (Chunky_texture &texture) { + texture.blit(from, to); }); + } }; #endif /* _RESIZEABLE_TEXTURE_H_ */ diff --git a/repos/os/src/server/nitpicker/view.cc b/repos/os/src/server/nitpicker/view.cc index 1a580886cd..ed233f6909 100644 --- a/repos/os/src/server/nitpicker/view.cc +++ b/repos/os/src/server/nitpicker/view.cc @@ -126,9 +126,32 @@ void Nitpicker::View::draw(Canvas_base &canvas, Font const &font, Focus const &f owner_color.g >> 1, owner_color.b >> 1); + auto for_each_tile_pos = [&] (auto const &fn) + { + int const view_w = view_rect.w(), + view_h = view_rect.h(); + + int const texture_w = int(_texture.size().w), + texture_h = int(_texture.size().h); + + if (!texture_w || !texture_h) + return; + + int off_x = (_buffer_off.x - _texture.panning.x) % texture_w; + int off_y = (_buffer_off.y - _texture.panning.y) % texture_h; + + if (off_x > 0) off_x -= texture_w; + if (off_y > 0) off_y -= texture_h; + + for (int y = off_y; y < view_h; y += texture_h) + for (int x = off_x; x < view_w; x += texture_w) + fn(Point { x, y }); + }; + _texture.with_texture([&] (Texture_base const &texture) { - canvas.draw_texture(_buffer_off + view_rect.p1(), texture, op, - mix_color, allow_alpha); }); + for_each_tile_pos([&] (Point const pos) { + canvas.draw_texture(view_rect.p1() + pos, texture, op, + mix_color, allow_alpha); }); }); if (!_texture.valid()) canvas.draw_box(view_rect, Color::black()); @@ -163,7 +186,7 @@ bool Nitpicker::View::input_response_at(Point const p) const /* if view uses an alpha channel, check the input mask */ if (_owner.content_client() && _owner.uses_alpha()) - return _owner.input_mask_at(p - view_rect.p1() - _buffer_off); + return _owner.input_mask_at(p - view_rect.p1() - _buffer_off + _texture.panning); return true; } diff --git a/repos/os/src/server/nitpicker/view_stack.h b/repos/os/src/server/nitpicker/view_stack.h index bc3567ca68..612c71fe1a 100644 --- a/repos/os/src/server/nitpicker/view_stack.h +++ b/repos/os/src/server/nitpicker/view_stack.h @@ -151,7 +151,8 @@ class Nitpicker::View_stack * Determine view portion that displays the buffer portion * specified by 'rect'. */ - Point const offset = view->abs_position() + view->buffer_off(); + Point const offset = view->abs_position() + view->buffer_off() + - session.panning(); Rect const r = Rect::intersect(Rect::compound(rect.p1() + offset, rect.p2() + offset), view->abs_geometry());