genode/repos/demo/src/app/scout/png_image.cc

159 lines
3.4 KiB
C++

/*
* \brief PNG image element
* \date 2005-11-07
* \author Norman Feske <norman.feske@genode-labs.com>
*/
/*
* Copyright (C) 2005-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <png.h>
#include <scout/misc_math.h>
#include <mini_c/init.h>
#include "elements.h"
using namespace Scout;
static Genode::Allocator *_alloc_ptr;
static Genode::Allocator &alloc()
{
if (_alloc_ptr)
return *_alloc_ptr;
Genode::error("missing call of Png_image::init");
class Png_image_init_missing { };
throw Png_image_init_missing();
}
class Png_stream
{
private:
char *_addr;
public:
/**
* Constructor
*/
Png_stream(char *addr) : _addr(addr) { }
/**
* Read from png stream
*/
void read(char *dst, int len)
{
Scout::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);
}
void Png_image::init(Genode::Allocator &alloc)
{
_alloc_ptr = &alloc;
mini_c_init(alloc);
}
/***********************
** Element interface **
***********************/
void Png_image::fill_cache(Canvas_base &canvas)
{
if (_texture) return;
Png_stream *stream = new (alloc()) 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 w, h;
png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
&interlace_type, int_p_NULL, int_p_NULL);
_min_size = Scout::Area(w, h);
log("png is ", _min_size, " depth=", 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);
_texture = canvas.alloc_texture(_min_size, true);
/* 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) alloc().free(row_ptr, curr_row_size);
row_ptr = (png_byte *)alloc().alloc(needed_row_size);
curr_row_size = needed_row_size;
}
/* fill texture */
for (unsigned j = 0; j < _min_size.h(); j++) {
png_read_row(png_ptr, row_ptr, NULL);
canvas.set_rgba_texture(_texture, (unsigned char *)row_ptr, _min_size.w(), j);
}
}
void Png_image::flush_cache(Canvas_base &canvas)
{
canvas.free_texture(_texture);
_texture = 0;
}
void Png_image::draw(Canvas_base &canvas, Point abs_position)
{
/* if texture is not ready, try to initialize it */
if (!_texture) fill_cache(canvas);
/* draw texture */
if (_texture)
canvas.draw_texture(abs_position + _position, *_texture);
}