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_ */