genode/repos/demo/src/app/backdrop/main.cc

270 lines
7.4 KiB
C++

/*
* \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;
}