gems: new backdrop application

The new backdrop found at gems/src/app/backdrop replaces the old program
that was hosted in the demo repository.
This commit is contained in:
Norman Feske 2014-08-14 16:25:29 +02:00
parent 6a46dcd2af
commit 61b370ecba
16 changed files with 1100 additions and 311 deletions

View File

@ -1,19 +0,0 @@
This directory contains a simple backdrop program for Nitpicker.
Usage
-----
You have to specify the name of the PNG file to be used as background
image via a declaration in your config file:
! <config>
! <image>background.png</image>
! </config>
Limitations
-----------
The PNG file is expected to be equal to the screen size. No scaling
or tiling is supported.

View File

@ -1,269 +0,0 @@
/*
* \brief Backdrop for Nitpicker
* \author Norman Feske
* \date 2009-08-28
*/
/*
* Copyright (C) 2009-2013 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.
*/
/* libpng includes */
#include <png.h>
/* Genode includes */
#include <nitpicker_session/connection.h>
#include <framebuffer_session/client.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <util/misc_math.h>
#include <os/config.h>
using namespace Genode;
/***************
** Dithering **
***************/
enum { DITHER_SIZE = 16, DITHER_MASK = DITHER_SIZE - 1 };
static const int dither_matrix[DITHER_SIZE][DITHER_SIZE] = {
{ 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 },
{ 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
{ 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
{ 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
{ 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 },
{ 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
{ 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
{ 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
{ 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 },
{ 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
{ 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
{ 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
{ 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 },
{ 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
{ 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
{ 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
};
static inline uint16_t rgb565(int r, int g, int b)
{
enum {
R_MASK = 0xf800, R_LSHIFT = 8,
G_MASK = 0x07e0, G_LSHIFT = 3,
B_MASK = 0x001f, B_RSHIFT = 3
};
return ((r << R_LSHIFT) & R_MASK)
| ((g << G_LSHIFT) & G_MASK)
| ((b >> B_RSHIFT) & B_MASK);
}
static void convert_line_rgba_to_rgb565(const unsigned char *rgba_src,
uint16_t *dst, int num_pixels, int line)
{
enum { CHANNEL_MAX = 255 };
int const *dm = dither_matrix[line & DITHER_MASK];
for (int i = 0; i < num_pixels; i++) {
int v = dm[i & DITHER_MASK] >> 5;
*dst++ = rgb565(min(v + (int)rgba_src[0], (int)CHANNEL_MAX),
min(v + (int)rgba_src[1], (int)CHANNEL_MAX),
min(v + (int)rgba_src[2], (int)CHANNEL_MAX));
/* we ignore the alpha channel */
rgba_src += 4; /* next pixel */
}
}
/************************
** PNG image decoding **
************************/
class Png_stream
{
private:
char *_addr;
public:
/**
* Constructor
*/
Png_stream(char *addr) { _addr = addr; }
/**
* Read from png stream
*/
void read(char *dst, int len)
{
Genode::memcpy(dst, _addr, len);
_addr += len;
}
};
/**
* PNG read callback
*/
static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t len)
{
Png_stream *stream = (Png_stream *)png_get_io_ptr(png_ptr);
stream->read((char *)data, len);
}
static void convert_png_to_rgb565(void *png_data,
uint16_t *dst, int dst_w, int dst_h)
{
Png_stream *stream = new (env()->heap()) Png_stream((char *)png_data);
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
if (!png_ptr) return;
png_set_read_fn(png_ptr, stream, user_read_data);
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
return;
}
png_read_info(png_ptr, info_ptr);
/* get image data chunk */
int bit_depth, color_type, interlace_type;
png_uint_32 img_w, img_h;
png_get_IHDR(png_ptr, info_ptr, &img_w, &img_h, &bit_depth, &color_type,
&interlace_type, int_p_NULL, int_p_NULL);
printf("png is %d x %d, depth=%d\n", (int)img_w, (int)img_h, bit_depth);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_gray_1_2_4_to_8(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
if (bit_depth < 8) png_set_packing(png_ptr);
if (bit_depth == 16) png_set_strip_16(png_ptr);
/* allocate buffer for decoding a row */
static png_byte *row_ptr;
static int curr_row_size;
int needed_row_size = png_get_rowbytes(png_ptr, info_ptr)*8;
if (curr_row_size < needed_row_size) {
if (row_ptr) env()->heap()->free(row_ptr, curr_row_size);
row_ptr = (png_byte *)env()->heap()->alloc(needed_row_size);
curr_row_size = needed_row_size;
}
/* fill texture */
int dst_y = 0;
for (int j = 0; j < min((int)img_h, dst_h); j++, dst_y++) {
png_read_row(png_ptr, row_ptr, NULL);
convert_line_rgba_to_rgb565((unsigned char *)row_ptr, dst + dst_y*dst_w,
min(dst_w, (int)img_w), j);
}
}
/****************************
** Configuration handling **
****************************/
/**
* Determine PNG filename of image to be used as background
*
* \param dst destination buffer for storing the filename
* \param dst_len size of destination buffer
* \return 0 on success
*/
static int read_image_filename_from_config(char *dst, Genode::size_t dst_len)
{
try {
Xml_node image_xml = config()->xml_node().sub_node("image");
image_xml.value(dst, dst_len);
return 0;
} catch (Xml_node::Nonexistent_sub_node) {
printf("Error: Configuration has no 'image' declaration.\n");
return -2;
}
}
/******************
** Main program **
******************/
int main(int argc, char **argv)
{
enum { PNG_NAME_MAX = 128 };
static char png_name[PNG_NAME_MAX];
if (read_image_filename_from_config(png_name, sizeof(png_name)) < 0)
return -1;
printf("using PNG file \"%s\" as background\n", png_name);
static void *png_data;
try {
static Rom_connection png_rom(png_name);
png_data = env()->rm_session()->attach(png_rom.dataspace());
} catch (...) {
printf("Error: Could not obtain PNG image from ROM service\n");
return -2;
}
static Nitpicker::Connection nitpicker;
/* obtain physical screen size */
Framebuffer::Mode const mode = nitpicker.mode();
/* setup virtual framebuffer mode */
nitpicker.buffer(mode, false);
static Framebuffer::Session_client framebuffer(nitpicker.framebuffer_session());
Nitpicker::Session::View_handle view_handle = nitpicker.create_view();
if (mode.format() != Framebuffer::Mode::RGB565) {
printf("Error: Color mode %d not supported\n", (int)mode.format());
return -3;
}
/* make virtual framebuffer locally accessible */
uint16_t *fb = env()->rm_session()->attach(framebuffer.dataspace());
/* fill virtual framebuffer with decoded image data */
convert_png_to_rgb565(png_data, fb, mode.width(), mode.height());
/* display view behind all others */
typedef Nitpicker::Session::Command Command;
nitpicker.enqueue<Command::Background>(view_handle);
Nitpicker::Rect rect(Nitpicker::Point(),
Nitpicker::Area(mode.width(), mode.height()));
nitpicker.enqueue<Command::Geometry>(view_handle, rect);
nitpicker.enqueue<Command::To_back>(view_handle);
nitpicker.execute();
framebuffer.refresh(0, 0, mode.width(), mode.height());
sleep_forever();
return 0;
}

View File

@ -1,4 +0,0 @@
TARGET = backdrop
SRC_CC = main.cc
LIBS = base libpng_static libz_static mini_c config
CC_OPT += -DPNG_USER_CONFIG

View File

@ -9,6 +9,7 @@ set build_components {
server/nitpicker app/pointer server/report_rom
drivers/framebuffer drivers/pci drivers/input
test/nitpicker
app/backdrop
app/launchpad
server/nit_fb
}
@ -153,34 +154,58 @@ append config {
</any-service>
</route>
</start>
<start name="testnit">
<resource name="RAM" quantum="1M"/>
<route>
<any-service> <child name="wm"/> <parent/> <any-child/>
</any-service>
</route>
</start>
<start name="direct_testnit">
<binary name="testnit" />
<resource name="RAM" quantum="1M"/>
<route>
<any-service> <child name="nitpicker"/> <parent/> <any-child/>
</any-service>
</route>
</start>
<start name="launchpad">
<resource name="RAM" quantum="20M"/>
<config>
<launcher name="testnit" ram_quota="768K" />
<launcher name="nit_fb" ram_quota="7M">
<config width="500" height="400" />
</launcher>
<launcher name="nitpicker" ram_quota="1M" >
<config>
<domain name="default" layer="2" />
<policy label="" domain="default"/>
</config>
</launcher>
<launcher name="testnit" ram_quota="768K" />
</config>
<route>
<any-service> <child name="wm"/> <parent/> <any-child/>
</any-service>
</route>
</start>
<start name="nit_fb">
<resource name="RAM" quantum="20M"/>
<config width="400" height="300" />
<start name="backdrop">
<resource name="RAM" quantum="4M"/>
<config>
<libc>
<vfs>
<rom name="genode_logo.png"/>
<rom name="grid.png"/>
</vfs>
</libc>
<fill color="#224433" />
<image png="grid.png" tiled="yes" alpha="200" />
<image png="genode_logo.png" anchor="bottom_right" alpha="150"
xpos="-20" ypos="-20" />
</config>
<route>
<any-service> <child name="nitpicker"/> <parent/> <any-child/>
</any-service>
</route>
</start>
<start name="wm_backdrop">
<binary name="backdrop" />
<resource name="RAM" quantum="4M"/>
<config>
<libc>
<vfs>
<rom name="genode_logo.png"/>
</vfs>
</libc>
<fill color="#664455" />
<image png="genode_logo.png" scale="zoom" alpha="150" />
<image png="genode_logo.png" scale="fit" alpha="150" tiled="yes" />
<image png="genode_logo.png" />
</config>
<route>
<any-service> <child name="wm"/> <parent/> <any-child/>
</any-service>
@ -190,6 +215,10 @@ append config {
install_config $config
# copy backdrop PNG images to bin directory
foreach file { genode_logo.png grid.png } {
file copy -force [genode_dir]/repos/gems/src/app/backdrop/$file bin/ }
#
# Boot modules
@ -202,7 +231,10 @@ set boot_modules {
wm decorator
floating_window_layouter
nitpicker pointer report_rom
backdrop
testnit launchpad nit_fb
ld.lib.so libpng.lib.so libc.lib.so libm.lib.so zlib.lib.so
genode_logo.png grid.png
}
# platform-specific modules

View File

@ -0,0 +1,76 @@
This directory contains a backdrop program for Nitpicker. It composes a
background image out of a solid color and an arbitrary number of PNG image
files. It is able to dynamically respond to configuration changes as well
as a changed screen size.
Configuration
~~~~~~~~~~~~~
A typical example configuration looks as follows.
! <config>
! <libc>
! <vfs>
! <rom name="genode_logo.png"/>
! <rom name="grid.png"/>
! </vfs>
! </libc>
! <fill color="#224433" />
! <image png="grid.png" tiled="yes" alpha="200" />
! <image png="genode_logo.png" anchor="bottom_right" alpha="150"
! xpos="-20" ypos="-20" />
! </config>
Backdrop obtains PNG images file from the libc VFS. In the example, the VFS is
configured to present two ROM modules as files in the root directory. Those
files are then referred to by the subsequent '<image>' nodes.
The final background image is generated by applying a number of graphical
operations in the order of their appearance in the configuration. The
'<fill>' operation fills the entire screen with the solid color as
specified by the 'color' attribute. The default color is black.
The '<image>' operation loads a PNG image from the VFS, scales and positions
it according to the '<image>' attributes, and paints it either as tiles
covering the whole screen or as a single image. The supported attributes are
:tiled (default "no"):
If set to "yes", the image will be used as tile to cover the entire screen.
:alpha (default is opaque):
A number in the range of 0..255 that specifies the opacity of the image
when painted.
:anchor (default "center"):
Defines the screen position that is used as a basis for positioning the
image. Supported values are "top_left", "top", "top_right", "left",
"center", "right", "bottom_left", "bottom", and "bottom_right".
In the example above, the _genode_logo.png_ image will be positioned
in the bottom-right corner of the screen.
:xpos and ypos (default "0"):
The 'xpos' and 'ypos' attributes define the position of the image relative
to the anchor. It is meant for allowing the pixel-perfect fine tuning of
positions.
:scale (no scaling by default):
The 'attribute' specifies the way of how the image should be adjusted to
the screen size by the means of proportional scaling. Possible values are
"fit" and "zoom". The former scales the image such that the entire image
is visible on screen. The latter makes sure that the entire screen is
filled with (a viewport of) the image. When using the "zoom" value, the
viewport can be defined via the 'anchor', 'xpos', and 'ypos' attributes.
Example
~~~~~~~
Please refer to the _gems/run/wm.run_ script for a practical example of
using the backdrop.

View File

@ -0,0 +1,62 @@
/*
* \brief Texture with backing store for pixels and alpha channel
* \author Norman Feske
* \date 2014-08-22
*/
/*
* Copyright (C) 2014 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.
*/
#ifndef _CHUNKY_TEXTURE_H_
#define _CHUNKY_TEXTURE_H_
#include <os/surface.h>
#include <os/attached_ram_dataspace.h>
template <typename PT>
class Chunky_texture : Genode::Attached_ram_dataspace, public Genode::Texture<PT>
{
private:
typedef Genode::Surface_base::Area Area;
/**
* Calculate memory needed to store the texture
*/
static Genode::size_t _num_bytes(Area size)
{
/* account for pixel size + 1 byte per alpha value */
return size.count()*(sizeof(PT) + 1);
}
/**
* Return base of pixel buffer
*/
PT *_pixel()
{
return local_addr<PT>();
}
/**
* Return base of alpha buffer
*/
unsigned char *_alpha(Area size)
{
/* alpha buffer follows pixel buffer */
return (unsigned char *)(local_addr<PT>() + size.count());
}
public:
Chunky_texture(Genode::Ram_session &ram, Genode::Surface_base::Area size)
:
Genode::Attached_ram_dataspace(&ram, _num_bytes(size)),
Genode::Texture<PT>(_pixel(), _alpha(size), size)
{ }
};
#endif /* _CHUNKY_TEXTURE_H_ */

View File

@ -0,0 +1,55 @@
/*
* \brief Utility for loading a file
* \author Norman Feske
* \date 2014-08-14
*/
/*
* Copyright (C) 2014 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 <base/printf.h>
/* local includes */
#include "file.h"
/* libc includes */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
static Genode::size_t file_size(char const *name)
{
struct stat s;
s.st_size = 0;
stat(name, &s);
return s.st_size;
}
File::File(char const *name, Genode::Allocator &alloc)
:
_alloc(alloc),
_file_size(file_size(name)),
_data(alloc.alloc(_file_size))
{
int const fd = open(name, O_RDONLY);
if (read(fd, _data, _file_size) < 0) {
PERR("reading from file \"%s\" failed (error %d)", name, errno);
throw Reading_failed();
}
close(fd);
}
File::~File()
{
_alloc.free(_data, _file_size);
}

View File

@ -0,0 +1,50 @@
/*
* \brief Utility for loading a file
* \author Norman Feske
* \date 2014-08-14
*/
/*
* Copyright (C) 2014 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.
*/
#ifndef _FILE_H_
#define _FILE_H_
/* Genode includes */
#include <base/allocator.h>
class File
{
private:
Genode::Allocator &_alloc;
Genode::size_t const _file_size;
void *_data;
public:
/**
* Exception type
*/
class Reading_failed { };
/**
* Constructor
*
* \throw Reading_failed;
*/
File(char const *name, Genode::Allocator &alloc);
~File();
template <typename T> T *data() { return (T *)_data; }
Genode::size_t size() const { return _file_size; }
};
#endif /* _FILE_H_ */

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

View File

@ -0,0 +1,375 @@
/*
* \brief Backdrop for Nitpicker
* \author Norman Feske
* \date 2009-08-28
*/
/*
* Copyright (C) 2009-2014 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 <nitpicker_session/connection.h>
#include <base/printf.h>
#include <util/misc_math.h>
#include <os/config.h>
#include <decorator/xml_utils.h>
#include <nitpicker_gfx/box_painter.h>
#include <nitpicker_gfx/texture_painter.h>
#include <os/attached_dataspace.h>
#include <util/volatile_object.h>
#include <os/pixel_rgb565.h>
#include <os/pixel_rgb888.h>
/* local includes */
#include "png_image.h"
#include "file.h"
#include "xml_anchor.h"
#include "texture_utils.h"
using namespace Genode;
namespace Backdrop { struct Main; }
struct Backdrop::Main
{
Nitpicker::Connection nitpicker;
struct Buffer
{
Nitpicker::Connection &nitpicker;
/* physical screen size */
Framebuffer::Mode const mode = nitpicker.mode();
/**
* Return dataspace capability for virtual framebuffer
*/
Dataspace_capability _ds_cap(Nitpicker::Connection &nitpicker)
{
/* setup virtual framebuffer mode */
nitpicker.buffer(mode, false);
if (mode.format() != Framebuffer::Mode::RGB565) {
PWRN("Color mode %d not supported\n", (int)mode.format());
return Dataspace_capability();
}
return nitpicker.framebuffer()->dataspace();
}
Attached_dataspace fb_ds { _ds_cap(nitpicker) };
size_t surface_num_bytes() const
{
return size().count()*mode.bytes_per_pixel();
}
Attached_ram_dataspace surface_ds { env()->ram_session(), surface_num_bytes() };
/**
* Constructor
*/
Buffer(Nitpicker::Connection &nitpicker) : nitpicker(nitpicker) { }
/**
* Return size of virtual framebuffer
*/
Surface_base::Area size() const
{
return Surface_base::Area(mode.width(), mode.height());
}
/**
* Return back buffer as painting surface
*/
template <typename PT>
Surface<PT> surface()
{
return Surface<PT>(surface_ds.local_addr<PT>(), size());
}
void flush_surface()
{
/* blit back to front buffer */
blit(surface_ds.local_addr<void>(), surface_num_bytes(),
fb_ds.local_addr<void>(), surface_num_bytes(), surface_num_bytes(), 1);
}
};
Lazy_volatile_object<Buffer> buffer;
Nitpicker::Session::View_handle view_handle = nitpicker.create_view();
void _update_view()
{
/* display view behind all others */
typedef Nitpicker::Session::Command Command;
nitpicker.enqueue<Command::Background>(view_handle);
Nitpicker::Rect rect(Nitpicker::Point(), buffer->size());
nitpicker.enqueue<Command::Geometry>(view_handle, rect);
nitpicker.enqueue<Command::To_back>(view_handle);
nitpicker.execute();
}
Signal_receiver &sig_rec;
/**
* Function called on config change or mode change
*/
void handle_config(unsigned);
Signal_dispatcher<Main> config_dispatcher = {
sig_rec, *this, &Main::handle_config};
void handle_sync(unsigned);
Signal_dispatcher<Main> sync_dispatcher = {
sig_rec, *this, &Main::handle_sync};
template <typename PT>
void paint_texture(Surface<PT> &, Texture<PT> const &, Surface_base::Point, bool);
void apply_image(Xml_node);
void apply_fill(Xml_node);
Main(Signal_receiver &sig_rec) : sig_rec(sig_rec)
{
/* trigger application of initial config */
Signal_transmitter(config_dispatcher).submit();
nitpicker.mode_sigh(config_dispatcher);
}
};
/**
* Calculate designated image size with proportional scaling applied
*/
static Surface_base::Area calc_scaled_size(Xml_node operation,
Surface_base::Area image_size,
Surface_base::Area mode_size)
{
char const *attr = "scale";
if (!operation.has_attribute(attr))
return image_size;
/* prevent division by zero, below */
if (image_size.count() == 0)
return image_size;
/*
* Determine scale ratio (in 16.16 fixpoint format)
*/
unsigned const ratio =
operation.attribute(attr).has_value("fit") ?
min((mode_size.w() << 16) / image_size.w(),
(mode_size.h() << 16) / image_size.h()) :
operation.attribute(attr).has_value("zoom") ?
max((mode_size.w() << 16) / image_size.w(),
(mode_size.h() << 16) / image_size.h()) :
1 << 16;
/*
* We add 0.5 (1 << 15) to round instead of truncating the fractional
* part when converting the fixpoint numbers to integers.
*/
return Surface_base::Area((image_size.w()*ratio + (1 << 15)) >> 16,
(image_size.h()*ratio + (1 << 15)) >> 16);
}
template <typename PT>
void Backdrop::Main::paint_texture(Surface<PT> &surface, Texture<PT> const &texture,
Surface_base::Point pos, bool tiled)
{
/* prevent division by zero */
if (texture.size().count() == 0)
return;
if (tiled) {
/* shortcuts */
int const w = texture.size().w(), surface_w = surface.size().w();
int const h = texture.size().h(), surface_h = surface.size().h();
/* draw tiles across the whole surface */
for (int y = (pos.y() % h) - h; y < surface_h + h; y += h)
for (int x = (pos.x() % w) - w; x < surface_w + w; x += w)
Texture_painter::paint(surface, texture, Color(),
Texture_painter::Point(x, y),
Texture_painter::SOLID,
true);
} else {
Texture_painter::paint(surface, texture, Color(), pos,
Texture_painter::SOLID, true);
}
}
void Backdrop::Main::apply_image(Xml_node operation)
{
typedef Surface_base::Point Point;
typedef Surface_base::Area Area;
if (!operation.has_attribute("png")) {
PWRN("missing 'png' attribute in <image> node");
return;
}
char png_file_name[256];
png_file_name[0] = 0;
operation.attribute("png").value(png_file_name, sizeof(png_file_name));
File file(png_file_name, *env()->heap());
Anchor anchor(operation);
Png_image png_image(file.data<void>());
Area const scaled_size = calc_scaled_size(operation, png_image.size(),
Area(buffer->mode.width(),
buffer->mode.height()));
/*
* Determine parameters of graphics operation
*/
int const h_gap = (int)buffer->mode.width() - scaled_size.w(),
v_gap = (int)buffer->mode.height() - scaled_size.h();
int const anchored_xpos = anchor.horizontal == Anchor::LOW ? 0
: anchor.horizontal == Anchor::CENTER ? h_gap/2
: anchor.horizontal == Anchor::HIGH ? h_gap
: 0;
int const anchored_ypos = anchor.vertical == Anchor::LOW ? 0
: anchor.vertical == Anchor::CENTER ? v_gap/2
: anchor.vertical == Anchor::HIGH ? v_gap
: 0;
Point const offset = Decorator::point_attribute(operation);
Point const pos = Point(anchored_xpos, anchored_ypos) + offset;
bool const tiled = operation.has_attribute("tiled")
&& operation.attribute("tiled").has_value("yes");
unsigned alpha = Decorator::attribute(operation, "alpha", 256U);
/* obtain texture containing the pixels of the PNG image */
Texture<Pixel_rgb888> *png_texture = png_image.texture<Pixel_rgb888>();
/* create texture with the scaled image */
Chunky_texture<Pixel_rgb888> scaled_texture(*env()->ram_session(), scaled_size);
scale(*png_texture, scaled_texture);
png_image.release_texture(png_texture);
/*
* Code specific for the screen mode's pixel format
*/
/* create texture with down-sampled scaled image */
typedef Pixel_rgb565 PT;
Chunky_texture<PT> texture(*env()->ram_session(), scaled_size);
convert_pixel_format(scaled_texture, texture, alpha);
/* paint texture onto surface */
Surface<PT> surface = buffer->surface<PT>();
paint_texture(surface, texture, pos, tiled);
}
void Backdrop::Main::apply_fill(Xml_node operation)
{
/*
* Code specific for the screen mode's pixel format
*/
/* create texture with down-sampled scaled image */
typedef Pixel_rgb565 PT;
Surface<PT> surface = buffer->surface<PT>();
Color const color = Decorator::attribute(operation, "color", Color(0, 0, 0));
Box_painter::paint(surface, Surface_base::Rect(Surface_base::Point(0, 0),
buffer->size()), color);
}
void Backdrop::Main::handle_config(unsigned)
{
config()->reload();
buffer.construct(nitpicker);
/* clear surface */
apply_fill(Xml_node("<fill color=\"#000000\"/>"));
/* apply graphics primitives defined in the config */
try {
for (unsigned i = 0; i < config()->xml_node().num_sub_nodes(); i++) {
try {
Xml_node operation = config()->xml_node().sub_node(i);
if (operation.has_type("image"))
apply_image(operation);
if (operation.has_type("fill"))
apply_fill(operation);
}
catch (...) {
/*
* Ignore failure of individual operation, i.e., non-existing
* files or malformed PNG data.
*/
}
}
} catch (...) { /* ignore failure to obtain config */ }
/* schedule buffer refresh */
nitpicker.framebuffer()->sync_sigh(sync_dispatcher);
}
void Backdrop::Main::handle_sync(unsigned)
{
buffer->flush_surface();
_update_view();
/* disable sync signal until the next call of 'handle_config' */
nitpicker.framebuffer()->sync_sigh(Signal_context_capability());
}
/*
* Silence debug messages
*/
extern "C" void _sigprocmask() { }
int main(int argc, char **argv)
{
static Signal_receiver sig_rec;
static Backdrop::Main application(sig_rec);
/* process incoming signals */
for (;;) {
using namespace Genode;
Signal sig = sig_rec.wait_for_signal();
Signal_dispatcher_base *dispatcher =
dynamic_cast<Signal_dispatcher_base *>(sig.context());
if (dispatcher)
dispatcher->dispatch(sig.num());
}
}

View File

@ -0,0 +1,181 @@
/*
* \brief Utility for reading PNG images
* \author Norman Feske
* \date 2014-08-14
*/
/*
* Copyright (C) 2014 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.
*/
#ifndef _PNG_IMAGE_H_
#define _PNG_IMAGE_H_
/* Genode includes */
#include <os/texture.h>
/* libpng include */
#include <png.h>
/* local includes */
#include "chunky_texture.h"
#include "texture_utils.h"
class Png_image
{
public:
/**
* Exception types
*/
class Read_struct_failed { };
class Info_failed { };
private:
template <typename EXC, typename T>
static T _assert_non_null(T && arg)
{
if (!arg)
throw EXC();
return arg;
};
struct Read_struct
{
/* start of PNG data */
png_bytep const data;
/* read position, maintained by 'read_callback' */
unsigned pos = 0;
static void callback(png_structp png_ptr, png_bytep dst, png_size_t len)
{
Png_image *png = (Png_image *)png_get_io_ptr(png_ptr);
Genode::memcpy(dst, png->_read_struct.data + png->_read_struct.pos, len);
png->_read_struct.pos += len;
}
png_structp png_ptr =
_assert_non_null<Read_struct_failed>(
png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0));
Read_struct(void *data) : data((png_bytep)data)
{
png_set_read_fn(png_ptr, this, callback);
}
~Read_struct()
{
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
}
};
Read_struct _read_struct;
struct Info
{
png_structp png_ptr;
png_infop info_ptr;
int bit_depth, color_type, interlace_type;
png_uint_32 img_w, img_h;
Info(png_structp png_ptr)
:
png_ptr(png_ptr),
info_ptr(_assert_non_null<Info_failed>(png_create_info_struct(png_ptr)))
{
png_read_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &img_w, &img_h, &bit_depth, &color_type,
&interlace_type, nullptr, nullptr);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
if (bit_depth < 8) png_set_packing(png_ptr);
if (bit_depth == 16) png_set_strip_16(png_ptr);
}
~Info()
{
png_destroy_info_struct(png_ptr, &info_ptr);
}
} _info { _read_struct.png_ptr };
struct Row
{
size_t const row_num_bytes;
png_bytep const row_ptr;
Row(png_structp png_ptr, png_infop info_ptr)
:
row_num_bytes(png_get_rowbytes(png_ptr, info_ptr)*8),
row_ptr((png_bytep)Genode::env()->heap()->alloc(row_num_bytes))
{ }
~Row()
{
Genode::env()->heap()->free(row_ptr, row_num_bytes);
}
} _row { _read_struct.png_ptr, _info.info_ptr };
public:
/**
* Constructor
*
* \throw Read_struct_failed
* \throw Info_failed
*/
Png_image(void *data) : _read_struct(data) { }
/**
* Return size of PNG image
*/
Genode::Surface_base::Area size() const
{
return Genode::Surface_base::Area(_info.img_w, _info.img_h);
}
/**
* Obtain PNG image as texture
*/
template <typename PT>
Genode::Texture<PT> *texture()
{
Genode::Texture<PT> *texture = new (Genode::env()->heap())
Chunky_texture<PT>(*Genode::env()->ram_session(), size());
/* fill texture with PNG image data */
for (unsigned i = 0; i < size().h(); i++) {
png_read_row(_read_struct.png_ptr, _row.row_ptr, NULL);
texture->rgba((unsigned char *)_row.row_ptr, size().w()*4, i);
}
return texture;
}
/**
* Free texture obtained via 'texture()'
*/
template <typename PT>
void release_texture(Genode::Texture<PT> *texture)
{
Chunky_texture<PT> *chunky_texture =
static_cast<Chunky_texture<PT> *>(texture);
Genode::destroy(Genode::env()->heap(), chunky_texture);
}
};
#endif /* _PNG_IMAGE_H_ */

View File

@ -0,0 +1,3 @@
TARGET = backdrop
SRC_CC = main.cc file.cc texture.cc
LIBS = base config libc libpng zlib blit

View File

@ -0,0 +1,79 @@
/*
* \brief Support for Genode::Texture
* \author Norman Feske
* \date 2014-08-14
*/
/*
* Copyright (C) 2014 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 <os/texture.h>
#include <os/pixel_rgb565.h>
#include <os/pixel_rgb888.h>
#include <util/dither_matrix.h>
namespace Genode {
template <>
void
Texture<Pixel_rgb565>::rgba(unsigned char const *rgba, unsigned len, int y)
{
if (len > size().w()) len = size().w();
if (y < 0 || y >= (int)size().h()) return;
Pixel_rgb565 *dst_pixel = pixel() + y*size().w();
unsigned char *dst_alpha = alpha() ? alpha() + y*size().w() : 0;
Dither_matrix::Row dither_row = Dither_matrix::row(y);
for (unsigned i = 0; i < len; i++) {
int v = dither_row.value(i) >> 5;
int r = *rgba++ + v;
int g = *rgba++ + v;
int b = *rgba++ + v;
int a = *rgba++;
if (a) a += v;
dst_pixel[i].rgba(min(r, 255), min(g, 255), min(b, 255));
if (dst_alpha)
dst_alpha[i] = min(a, 255);
}
}
template <>
void
Texture<Pixel_rgb888>::rgba(unsigned char const *rgba, unsigned len, int y)
{
if (len > size().w()) len = size().w();
if (y < 0 || y >= (int)size().h()) return;
Pixel_rgb888 *dst_pixel = pixel() + y*size().w();
unsigned char *dst_alpha = alpha() ? alpha() + y*size().w() : 0;
for (unsigned i = 0; i < len; i++) {
int r = *rgba++;
int g = *rgba++;
int b = *rgba++;
int a = *rgba++;
dst_pixel[i].rgba(r, g, b);
if (dst_alpha)
dst_alpha[i] = min(a, 255);
}
}
}
template class Genode::Texture<Genode::Pixel_rgb565>;
template class Genode::Texture<Genode::Pixel_rgb888>;

View File

@ -0,0 +1,97 @@
/*
* \brief Utilities for working with textures
* \author Norman Feske
* \date 2014-08-21
*/
/*
* Copyright (C) 2014 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.
*/
#ifndef _TEXTURE_UTILS_H_
#define _TEXTURE_UTILS_H_
#include <os/texture.h>
template <typename PT>
static void scale(Genode::Texture<PT> const &src, Genode::Texture<PT> &dst)
{
/* sanity check to prevent division by zero */
if (dst.size().count() == 0)
return;
Genode::size_t const row_num_bytes = dst.size().w()*4;
unsigned char *row = (unsigned char *)Genode::env()->heap()->alloc(row_num_bytes);
unsigned const mx = (src.size().w() << 16) / dst.size().w();
unsigned const my = (src.size().h() << 16) / dst.size().h();
for (unsigned y = 0, src_y = 0; y < dst.size().h(); y++, src_y += my) {
unsigned const src_line_offset = src.size().w()*(src_y >> 16);
PT const *pixel_line = src.pixel() + src_line_offset;
unsigned char const *alpha_line = src.alpha() + src_line_offset;
unsigned char *d = row;
for (unsigned x = 0, src_x = 0; x < dst.size().w(); x++, src_x += mx) {
unsigned const pixel_offset = src_x >> 16;
PT const pixel = pixel_line[pixel_offset];
unsigned char const alpha = alpha_line[pixel_offset];
*d++ = pixel.r();
*d++ = pixel.g();
*d++ = pixel.b();
*d++ = alpha;
}
dst.rgba(row, dst.size().w(), y);
}
Genode::env()->heap()->free(row, row_num_bytes);
}
template <typename SRC_PT, typename DST_PT>
static void convert_pixel_format(Genode::Texture<SRC_PT> const &src,
Genode::Texture<DST_PT> &dst,
unsigned alpha)
{
/* sanity check */
if (src.size() != dst.size())
return;
Genode::size_t const row_num_bytes = dst.size().w()*4;
unsigned char *row = (unsigned char *)Genode::env()->heap()->alloc(row_num_bytes);
/* shortcuts */
unsigned const w = dst.size().w(), h = dst.size().h();
for (unsigned y = 0, line_offset = 0; y < h; y++, line_offset += w) {
SRC_PT const *src_pixel = src.pixel() + line_offset;
unsigned char const *src_alpha = src.alpha() + line_offset;
/* fill row buffer with values from source texture */
unsigned char *d = row;
for (unsigned x = 0; x < w; x++, src_pixel++, src_alpha++) {
*d++ = src_pixel->r();
*d++ = src_pixel->g();
*d++ = src_pixel->b();
*d++ = (*src_alpha * alpha) >> 8;
}
/* assign row to destination texture */
dst.rgba(row, w, y);
}
Genode::env()->heap()->free(row, row_num_bytes);
}
#endif /* _TEXTURE_UTILS_H_ */

View File

@ -0,0 +1,71 @@
/*
* \brief Utility for parsing an anchor attribute from an XML node
* \author Norman Feske
* \date 2014-08-21
*/
/*
* Copyright (C) 2014 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.
*/
#ifndef _XML_ANCHOR_H_
#define _XML_ANCHOR_H_
#include <util/xml_node.h>
class Anchor
{
public:
enum Direction { LOW, CENTER, HIGH };
Direction horizontal = CENTER, vertical = CENTER;
private:
typedef Genode::Xml_node Xml_node;
struct Value
{
char const *value;
Direction horizontal, vertical;
};
Value const _values[10] = { { "top_left", LOW, LOW },
{ "top", CENTER, LOW },
{ "top_right", HIGH, LOW },
{ "left", LOW, CENTER },
{ "center", CENTER, CENTER },
{ "right", HIGH, CENTER },
{ "bottom_left", LOW, HIGH },
{ "bottom", CENTER, HIGH },
{ "bottom_right", HIGH, HIGH },
{ nullptr, CENTER, CENTER } };
public:
Anchor(Xml_node node)
{
char const * const attr = "anchor";
if (!node.has_attribute(attr))
return;
Xml_node::Attribute const anchor = node.attribute(attr);
for (Value const *value = _values; value->value; value++) {
if (anchor.has_value(value->value)) {
horizontal = value->horizontal;
vertical = value->vertical;
return;
}
}
PWRN("unsupported anchor attribute value");
}
};
#endif /* _XML_ANCHOR_H_ */