server/fb_upscale: sever to scale up small framebuffers

Fixes #29
This commit is contained in:
Emery Hemingway
2016-08-16 08:01:06 +02:00
committed by Norman Feske
parent 10a42e8c14
commit bf93d7a9f0
3 changed files with 230 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
The 'fb_upscale' server scales a fixed-resolution framebuffer client session
to a larger parent framebuffer session. The scaling is linear at a ratio of
1:1 across the x and y axis. Clients must supply their native resolution at
the time of session creation.

View File

@@ -0,0 +1,223 @@
/*
* \brief Linear framebuffer upscaler
* \author Emery Hemingway
* \date 2016-07-20
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <framebuffer_session/connection.h>
#include <base/attached_ram_dataspace.h>
#include <base/attached_dataspace.h>
#include <root/component.h>
#include <base/component.h>
#include <base/log.h>
namespace Fb_scaler {
using namespace Framebuffer;
class Session_component;
class Root_component;
typedef
Genode::Root_component<Session_component, Genode::Single_client>
Root_component_base;
}
class Fb_scaler::Session_component : public Genode::Rpc_object<Framebuffer::Session>
{
private:
Genode::Env &_env;
Framebuffer::Connection _parent_fb { _env, Framebuffer::Mode() };
Framebuffer::Mode _parent_mode = _parent_fb.mode();
Framebuffer::Mode _client_mode;
Genode::Attached_ram_dataspace _client_ds
{ _env.ram(), _env.rm(),
Genode::size_t(_client_mode.width()*_client_mode.height())*2
};
Genode::Lazy_volatile_object<Genode::Attached_dataspace> _parent_ds;
Genode::Signal_context_capability _client_sig_cap;
enum { SHIFT = 16 };
int _scale_factor;
int _scale_ratio;
int _x_offset;
int _y_offset;
void _rescale()
{
/* get a new dataspace */
if (_parent_ds.constructed())
_parent_ds.destruct();
_parent_ds.construct(_env.rm(), _parent_fb.dataspace());
Genode::memset(_parent_ds->local_addr<char>(),
0x00, _parent_ds->size());
/* calculate the scaling using the FPU */
float x_factor =
float(_parent_mode.width()) /
float(_client_mode.width());
float y_factor =
float(_parent_mode.height()) /
float(_client_mode.height());
float factor = Genode::min(x_factor, y_factor);
_x_offset =
(_parent_mode.width() -
(_client_mode.width()*factor)) / 2;
_y_offset =
(_parent_mode.height() -
(_client_mode.height()*factor)) / 2;
/* shift so the scaling can be done with integeral math */
_scale_factor = (1<<SHIFT)*factor;
_scale_ratio = ((1 << SHIFT) / factor)+1;
}
void _handle_mode()
{
_parent_mode = _parent_fb.mode();
if (_parent_mode.width() && _parent_mode.height()) {
_rescale();
} else {
/* notify the client of the null mode */
if (_client_sig_cap.valid())
Genode::Signal_transmitter(_client_sig_cap).submit();
}
}
Genode::Signal_handler<Session_component> _mode_handler
{ _env.ep(), *this, &Session_component::_handle_mode };
public:
Session_component(Genode::Env &env, Mode client_mode)
:
_env(env),
_client_mode(client_mode.width() && client_mode.height() ?
client_mode : _parent_mode)
{
if (!(_client_mode.width() && _client_mode.height())) {
/* use the parent mode maybe enlarge later */
_client_mode = _parent_mode;
_handle_mode();
}
_rescale();
_parent_fb.mode_sigh(_mode_handler);
}
/***********************************
** Framebuffer session interface **
***********************************/
Genode::Dataspace_capability dataspace() override {
return _client_ds.cap(); }
Mode mode() const override
{
Mode parent_mode = _parent_fb.mode();
return (!parent_mode.width() && !parent_mode.height()) ?
parent_mode : _client_mode;
}
void refresh(int cx, int cy, int cw, int ch) override
{
using Genode::uint16_t;
/* the shifting is to round down by dropping bits */
int px = (cx * _scale_factor)>>SHIFT;
int py = (cy * _scale_factor)>>SHIFT;
int pw = (cw * _scale_factor)>>SHIFT;
int ph = (ch * _scale_factor)>>SHIFT;
uint16_t const *src = _client_ds.local_addr<uint16_t const>();
uint16_t *dst = _parent_ds->local_addr<uint16_t>();
for (int y = py; y < ph; ++y) {
int src_y = ((y*_scale_ratio) >> SHIFT)*_client_mode.width();
for (int x = px; x < pw; ++x) {
dst[(_y_offset+y)*_parent_mode.width()+x+_x_offset] =
src[src_y+((x*_scale_ratio) >> SHIFT)];
}
}
_parent_fb.refresh(_x_offset+px, _y_offset+py,
_y_offset+pw, _x_offset+ph);
}
void mode_sigh(Genode::Signal_context_capability sig_cap) override {
_client_sig_cap = sig_cap; }
/* client gets sync signals from parent session */
void sync_sigh(Genode::Signal_context_capability sig_cap) override {
_parent_fb.sync_sigh(sig_cap); }
};
class Fb_scaler::Root_component : Root_component_base
{
private:
Genode::Env &_env;
protected:
Session_component *_create_session(char const *args) override
{
using namespace Genode;
unsigned width = Arg_string::find_arg(args, "fb_width").ulong_value(0);
unsigned height = Arg_string::find_arg(args, "fb_width").ulong_value(0);
return new (md_alloc())
Session_component(_env, Mode(width, height, Mode::INVALID));
}
public:
Root_component(Genode::Env &env, Genode::Allocator &md_alloc)
: Root_component_base(env.ep(), md_alloc),
_env(env)
{
env.parent().announce(env.ep().manage(*this));
}
};
/***************
** Component **
***************/
Genode::size_t Component::stack_size() { return 4*1024*sizeof(Genode::addr_t); }
void Component::construct(Genode::Env &env)
{
static Genode::Sliced_heap sliced_heap(env.ram(), env.rm());
static Fb_scaler::Root_component root(env, sliced_heap);
}

View File

@@ -0,0 +1,3 @@
TARGET = fb_upscale
SRC_CC = component.cc
LIBS = base