diff --git a/repos/demo/src/app/backdrop/README b/repos/demo/src/app/backdrop/README
deleted file mode 100644
index 3c1d2ab611..0000000000
--- a/repos/demo/src/app/backdrop/README
+++ /dev/null
@@ -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:
-
-!
-! background.png
-!
-
-
-Limitations
------------
-
-The PNG file is expected to be equal to the screen size. No scaling
-or tiling is supported.
diff --git a/repos/demo/src/app/backdrop/main.cc b/repos/demo/src/app/backdrop/main.cc
deleted file mode 100644
index bbd1269af0..0000000000
--- a/repos/demo/src/app/backdrop/main.cc
+++ /dev/null
@@ -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
-
-/* Genode includes */
-#include
-#include
-#include
-#include
-#include
-#include
-
-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(view_handle);
- Nitpicker::Rect rect(Nitpicker::Point(),
- Nitpicker::Area(mode.width(), mode.height()));
- nitpicker.enqueue(view_handle, rect);
- nitpicker.enqueue(view_handle);
- nitpicker.execute();
-
- framebuffer.refresh(0, 0, mode.width(), mode.height());
-
- sleep_forever();
- return 0;
-}
diff --git a/repos/demo/src/app/backdrop/target.mk b/repos/demo/src/app/backdrop/target.mk
deleted file mode 100644
index ef4c951a98..0000000000
--- a/repos/demo/src/app/backdrop/target.mk
+++ /dev/null
@@ -1,4 +0,0 @@
-TARGET = backdrop
-SRC_CC = main.cc
-LIBS = base libpng_static libz_static mini_c config
-CC_OPT += -DPNG_USER_CONFIG
diff --git a/repos/gems/run/wm.run b/repos/gems/run/wm.run
index 7bc8fdc0e1..5aff6d5c46 100644
--- a/repos/gems/run/wm.run
+++ b/repos/gems/run/wm.run
@@ -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 {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -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
diff --git a/repos/gems/src/app/backdrop/README b/repos/gems/src/app/backdrop/README
new file mode 100644
index 0000000000..ef0533282e
--- /dev/null
+++ b/repos/gems/src/app/backdrop/README
@@ -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.
+
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+
+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 '' nodes.
+
+The final background image is generated by applying a number of graphical
+operations in the order of their appearance in the configuration. The
+'' operation fills the entire screen with the solid color as
+specified by the 'color' attribute. The default color is black.
+
+The '' operation loads a PNG image from the VFS, scales and positions
+it according to the '' 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.
+
diff --git a/repos/gems/src/app/backdrop/chunky_texture.h b/repos/gems/src/app/backdrop/chunky_texture.h
new file mode 100644
index 0000000000..f7564da22f
--- /dev/null
+++ b/repos/gems/src/app/backdrop/chunky_texture.h
@@ -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
+#include
+
+template
+class Chunky_texture : Genode::Attached_ram_dataspace, public Genode::Texture
+{
+ 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();
+ }
+
+ /**
+ * Return base of alpha buffer
+ */
+ unsigned char *_alpha(Area size)
+ {
+ /* alpha buffer follows pixel buffer */
+ return (unsigned char *)(local_addr() + size.count());
+ }
+
+ public:
+
+ Chunky_texture(Genode::Ram_session &ram, Genode::Surface_base::Area size)
+ :
+ Genode::Attached_ram_dataspace(&ram, _num_bytes(size)),
+ Genode::Texture(_pixel(), _alpha(size), size)
+ { }
+};
+
+#endif /* _CHUNKY_TEXTURE_H_ */
diff --git a/repos/gems/src/app/backdrop/file.cc b/repos/gems/src/app/backdrop/file.cc
new file mode 100644
index 0000000000..23fc98734e
--- /dev/null
+++ b/repos/gems/src/app/backdrop/file.cc
@@ -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
+
+/* local includes */
+#include "file.h"
+
+/* libc includes */
+#include
+#include
+#include
+#include
+#include
+
+
+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);
+}
diff --git a/repos/gems/src/app/backdrop/file.h b/repos/gems/src/app/backdrop/file.h
new file mode 100644
index 0000000000..bf32b5b90f
--- /dev/null
+++ b/repos/gems/src/app/backdrop/file.h
@@ -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
+
+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 T *data() { return (T *)_data; }
+
+ Genode::size_t size() const { return _file_size; }
+};
+
+#endif /* _FILE_H_ */
+
diff --git a/repos/gems/src/app/backdrop/genode_logo.png b/repos/gems/src/app/backdrop/genode_logo.png
new file mode 100644
index 0000000000..816dfaf0a2
Binary files /dev/null and b/repos/gems/src/app/backdrop/genode_logo.png differ
diff --git a/repos/gems/src/app/backdrop/grid.png b/repos/gems/src/app/backdrop/grid.png
new file mode 100644
index 0000000000..f3e5ef1008
Binary files /dev/null and b/repos/gems/src/app/backdrop/grid.png differ
diff --git a/repos/gems/src/app/backdrop/main.cc b/repos/gems/src/app/backdrop/main.cc
new file mode 100644
index 0000000000..6a9bd4c8e9
--- /dev/null
+++ b/repos/gems/src/app/backdrop/main.cc
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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
+ Surface surface()
+ {
+ return Surface(surface_ds.local_addr(), size());
+ }
+
+ void flush_surface()
+ {
+ /* blit back to front buffer */
+ blit(surface_ds.local_addr(), surface_num_bytes(),
+ fb_ds.local_addr(), surface_num_bytes(), surface_num_bytes(), 1);
+ }
+ };
+
+ Lazy_volatile_object 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(view_handle);
+ Nitpicker::Rect rect(Nitpicker::Point(), buffer->size());
+ nitpicker.enqueue(view_handle, rect);
+ nitpicker.enqueue(view_handle);
+ nitpicker.execute();
+ }
+
+ Signal_receiver &sig_rec;
+
+ /**
+ * Function called on config change or mode change
+ */
+ void handle_config(unsigned);
+
+ Signal_dispatcher config_dispatcher = {
+ sig_rec, *this, &Main::handle_config};
+
+ void handle_sync(unsigned);
+
+ Signal_dispatcher sync_dispatcher = {
+ sig_rec, *this, &Main::handle_sync};
+
+ template
+ void paint_texture(Surface &, Texture 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
+void Backdrop::Main::paint_texture(Surface &surface, Texture 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 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());
+
+ 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 *png_texture = png_image.texture();
+
+ /* create texture with the scaled image */
+ Chunky_texture 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 texture(*env()->ram_session(), scaled_size);
+ convert_pixel_format(scaled_texture, texture, alpha);
+
+ /* paint texture onto surface */
+ Surface surface = buffer->surface();
+ 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 surface = buffer->surface();
+
+ 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(""));
+
+ /* 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(sig.context());
+
+ if (dispatcher)
+ dispatcher->dispatch(sig.num());
+ }
+}
diff --git a/repos/gems/src/app/backdrop/png_image.h b/repos/gems/src/app/backdrop/png_image.h
new file mode 100644
index 0000000000..4a9900269a
--- /dev/null
+++ b/repos/gems/src/app/backdrop/png_image.h
@@ -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
+
+/* libpng include */
+#include
+
+/* 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
+ 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(
+ 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(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
+ Genode::Texture *texture()
+ {
+ Genode::Texture *texture = new (Genode::env()->heap())
+ Chunky_texture(*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
+ void release_texture(Genode::Texture *texture)
+ {
+ Chunky_texture *chunky_texture =
+ static_cast *>(texture);
+
+ Genode::destroy(Genode::env()->heap(), chunky_texture);
+ }
+};
+
+#endif /* _PNG_IMAGE_H_ */
diff --git a/repos/gems/src/app/backdrop/target.mk b/repos/gems/src/app/backdrop/target.mk
new file mode 100644
index 0000000000..c7a61f9411
--- /dev/null
+++ b/repos/gems/src/app/backdrop/target.mk
@@ -0,0 +1,3 @@
+TARGET = backdrop
+SRC_CC = main.cc file.cc texture.cc
+LIBS = base config libc libpng zlib blit
diff --git a/repos/gems/src/app/backdrop/texture.cc b/repos/gems/src/app/backdrop/texture.cc
new file mode 100644
index 0000000000..0ecdf26b5c
--- /dev/null
+++ b/repos/gems/src/app/backdrop/texture.cc
@@ -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
+#include
+#include
+#include
+
+namespace Genode {
+
+ template <>
+ void
+ Texture::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::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;
+template class Genode::Texture;
diff --git a/repos/gems/src/app/backdrop/texture_utils.h b/repos/gems/src/app/backdrop/texture_utils.h
new file mode 100644
index 0000000000..e8fbf1159c
--- /dev/null
+++ b/repos/gems/src/app/backdrop/texture_utils.h
@@ -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
+
+template
+static void scale(Genode::Texture const &src, Genode::Texture &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
+static void convert_pixel_format(Genode::Texture const &src,
+ Genode::Texture &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_ */
diff --git a/repos/gems/src/app/backdrop/xml_anchor.h b/repos/gems/src/app/backdrop/xml_anchor.h
new file mode 100644
index 0000000000..2b42dbea60
--- /dev/null
+++ b/repos/gems/src/app/backdrop/xml_anchor.h
@@ -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
+
+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_ */