mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
framebuffer_session: atomic blitting and panning
By enhancing the Framebuffer::Session interface by the new RPC functions 'blit' and 'panning', GUI clients become able to attain tearing-free output. Two modes of operations are supported. 1. Atomic back-to-front blitting GUI clients that partially update their user interface like regular application dialogs, can now implement double buffering by placing both the back buffer and front buffer within the GUI session's framebuffer and configuring a view that shows only the front buffer. The 'blit' operation allows the client to atomically flush pixels from the back buffer to the front buffer. 2. Atomic buffer flipping GUI clients that always update all pixels like a media player or a game can now use the 'panning' feature to atomically redirect the displayed pixels to a different portion of the GUI session's virtual frame buffer. The virtual framebuffer always contains two frames, the displayed one and the next one. Once the next frame is complete, the client changes the panning position to the portion containing the next frame. Issue #5350
This commit is contained in:
parent
0e33830d1f
commit
8082aa980e
@ -266,6 +266,17 @@ class Framebuffer::Session_component : public Genode::Rpc_object<Session>
|
||||
{
|
||||
_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");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <os/static_root.h>
|
||||
#include <util/reconstructible.h>
|
||||
#include <nitpicker_gfx/texture_painter.h>
|
||||
#include <blit/painter.h>
|
||||
#include <util/lazy_value.h>
|
||||
#include <timer_session/connection.h>
|
||||
|
||||
@ -67,6 +68,7 @@ class Gui_fader::Src_buffer
|
||||
bool const _use_alpha;
|
||||
Attached_ram_dataspace _ds;
|
||||
Texture<Pixel> _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_rgb888> pixel { _ds.local_addr<Pixel_rgb888>(), _texture.size() };
|
||||
fn(pixel);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -93,6 +101,18 @@ class Gui_fader::Src_buffer
|
||||
Texture<Pixel> 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<Pixel_rgb888> &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> _dst_buffer { };
|
||||
|
||||
|
||||
Lazy_value<int> _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);
|
||||
|
@ -22,19 +22,15 @@ namespace Framebuffer { struct Session_client; }
|
||||
|
||||
struct Framebuffer::Session_client : Rpc_client<Session>
|
||||
{
|
||||
explicit Session_client(Session_capability session)
|
||||
: Rpc_client<Session>(session) { }
|
||||
explicit Session_client(Session_capability cap) : Rpc_client<Session>(cap) { }
|
||||
|
||||
Dataspace_capability dataspace() override {
|
||||
return call<Rpc_dataspace>(); }
|
||||
Dataspace_capability dataspace() override { return call<Rpc_dataspace>(); }
|
||||
|
||||
Mode mode() const override { return call<Rpc_mode>(); }
|
||||
|
||||
void mode_sigh(Signal_context_capability sigh) override {
|
||||
call<Rpc_mode_sigh>(sigh); }
|
||||
void mode_sigh(Signal_context_capability sigh) override { call<Rpc_mode_sigh>(sigh); }
|
||||
|
||||
void sync_sigh(Signal_context_capability sigh) override {
|
||||
call<Rpc_sync_sigh>(sigh); }
|
||||
void sync_sigh(Signal_context_capability sigh) override { call<Rpc_sync_sigh>(sigh); }
|
||||
|
||||
void refresh(Rect rect) override { call<Rpc_refresh>(rect); }
|
||||
|
||||
@ -49,6 +45,19 @@ struct Framebuffer::Session_client : Rpc_client<Session>
|
||||
refresh(Rect { { x, y }, { unsigned(w), unsigned(h) } });
|
||||
}
|
||||
|
||||
Blit_result blit(Blit_batch const &batch) override { return call<Rpc_blit>(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<Rpc_panning>(pos); }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__FRAMEBUFFER_SESSION__CLIENT_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_ */
|
||||
|
@ -131,6 +131,13 @@ struct Framebuffer::Session_component : Genode::Rpc_object<Framebuffer::Session>
|
||||
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<Framebuffer::Session>
|
||||
|
||||
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
|
||||
{
|
||||
/*
|
||||
|
@ -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_ */
|
||||
|
@ -14,6 +14,11 @@
|
||||
#ifndef _CHUNKY_TEXTURE_H_
|
||||
#define _CHUNKY_TEXTURE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <os/pixel_rgb888.h>
|
||||
#include <os/pixel_alpha8.h>
|
||||
#include <blit/painter.h>
|
||||
|
||||
/* local includes */
|
||||
#include <buffer.h>
|
||||
|
||||
@ -36,6 +41,64 @@ class Nitpicker::Chunky_texture : public Buffer, public Texture<PT>
|
||||
return (unsigned char *)local_addr() + calc_num_bytes(size, false);
|
||||
}
|
||||
|
||||
Area _area() const { return Texture<PT>::size(); }
|
||||
|
||||
void _with_pixel_surface(auto const &fn)
|
||||
{
|
||||
Surface<Pixel_rgb888> 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<PT>::alpha());
|
||||
if (ptr)
|
||||
fn(ptr);
|
||||
}
|
||||
|
||||
void _with_alpha_surface(auto const &fn)
|
||||
{
|
||||
_with_alpha_ptr(*this, [&] (Pixel_alpha8 * const ptr) {
|
||||
Surface<Pixel_alpha8> alpha { ptr, _area() };
|
||||
fn(alpha); });
|
||||
}
|
||||
|
||||
void _with_alpha_texture(auto const &fn) const
|
||||
{
|
||||
_with_alpha_ptr(*this, [&] (Pixel_alpha8 * const ptr) {
|
||||
Texture<Pixel_alpha8> 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<Pixel_alpha8> input { ptr, _area() };
|
||||
fn(input); });
|
||||
}
|
||||
|
||||
void _with_input_texture(auto const &fn) const
|
||||
{
|
||||
_with_input_ptr(*this, [&] (Pixel_alpha8 * const ptr) {
|
||||
Texture<Pixel_alpha8> texture { ptr, nullptr, _area() };
|
||||
fn(texture); });
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void _blit_channel(Surface<T> &surface, Texture<T> 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<PT>
|
||||
return (unsigned char const *)local_addr() + calc_num_bytes(size, false)
|
||||
+ size.count();
|
||||
}
|
||||
|
||||
void blit(Rect from, Point to)
|
||||
{
|
||||
_with_pixel_surface([&] (Surface<Pixel_rgb888> &surface) {
|
||||
_blit_channel(surface, *this, from, to); });
|
||||
|
||||
_with_alpha_surface([&] (Surface<Pixel_alpha8> &surface) {
|
||||
_with_alpha_texture([&] (Texture<Pixel_alpha8> &texture) {
|
||||
_blit_channel(surface, texture, from, to); }); });
|
||||
|
||||
_with_input_surface([&] (Surface<Pixel_alpha8> &surface) {
|
||||
_with_input_texture([&] (Texture<Pixel_alpha8> &texture) {
|
||||
_blit_channel(surface, texture, from, to); }); });
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _CHUNKY_TEXTURE_H_ */
|
||||
|
@ -116,6 +116,10 @@ class Framebuffer::Session_component : public Rpc_object<Session>
|
||||
}
|
||||
|
||||
void refresh(Rect) override;
|
||||
|
||||
Blit_result blit(Blit_batch const &) override;
|
||||
|
||||
void panning(Point) override;
|
||||
};
|
||||
|
||||
#endif /* _FRAMEBUFFER_SESSION_COMPONENT_H_ */
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include <base/heap.h>
|
||||
#include <os/session_policy.h>
|
||||
#include <os/reporter.h>
|
||||
#include <os/pixel_rgb888.h>
|
||||
#include <blit/painter.h>
|
||||
#include <gui_session/gui_session.h>
|
||||
|
||||
/* local includes */
|
||||
@ -292,6 +290,7 @@ class Nitpicker::Gui_session : public Session_object<Gui::Session>,
|
||||
|
||||
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<Gui::Session>,
|
||||
_forwarded_focus = nullptr;
|
||||
}
|
||||
|
||||
Point panning() const { return _texture.panning; }
|
||||
|
||||
|
||||
/***************************
|
||||
** GUI session interface **
|
||||
@ -416,6 +417,10 @@ class Nitpicker::Gui_session : public Session_object<Gui::Session>,
|
||||
*******************************/
|
||||
|
||||
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_ */
|
||||
|
@ -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 **
|
||||
***************************************/
|
||||
|
@ -14,10 +14,6 @@
|
||||
#ifndef _RESIZEABLE_TEXTURE_H_
|
||||
#define _RESIZEABLE_TEXTURE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <blit/painter.h>
|
||||
#include <os/pixel_alpha8.h>
|
||||
|
||||
/* local includes */
|
||||
#include <chunky_texture.h>
|
||||
|
||||
@ -31,17 +27,18 @@ class Nitpicker::Resizeable_texture
|
||||
|
||||
unsigned _current = 0;
|
||||
|
||||
using Constructible_texture = Constructible<Chunky_texture<PT>>;
|
||||
Constructible<Chunky_texture<PT>> _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 <typename FN>
|
||||
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<PT> &texture) {
|
||||
texture.blit(from, to); });
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _RESIZEABLE_TEXTURE_H_ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user