diff --git a/repos/gems/run/text_painter.run b/repos/gems/run/text_painter.run new file mode 100644 index 0000000000..46285ff531 --- /dev/null +++ b/repos/gems/run/text_painter.run @@ -0,0 +1,75 @@ +create_boot_directory + +import_from_depot genodelabs/src/[base_src] \ + genodelabs/pkg/[drivers_interactive_pkg] \ + genodelabs/src/init \ + genodelabs/src/libc \ + genodelabs/src/vfs \ + genodelabs/raw/ttf-bitstream-vera-minimal + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +build { server/vfs test/text_painter lib/vfs/ttf } + +build_boot_image { vfs test-text_painter vfs_ttf.lib.so } + +run_genode_until forever diff --git a/repos/gems/src/test/text_painter/main.cc b/repos/gems/src/test/text_painter/main.cc new file mode 100644 index 0000000000..48cf91e379 --- /dev/null +++ b/repos/gems/src/test/text_painter/main.cc @@ -0,0 +1,228 @@ +/* + * \brief Playground for painting text + * \author Norman Feske + * \date 2018-03-08 + */ + +/* + * Copyright (C) 2018 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. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* gems includes */ +#include +#include +#include +#include + +namespace Test { + using namespace Genode; + + typedef Surface_base::Point Point; + typedef Surface_base::Area Area; + typedef Surface_base::Rect Rect; + struct Main; +}; + + +/** + * Statically linked binary data + */ +extern char _binary_droidsansb10_tff_start[]; +extern char _binary_default_tff_start[]; + + +struct Test::Main +{ + Env &_env; + + Framebuffer::Connection _fb { _env, Framebuffer::Mode() }; + + Attached_dataspace _fb_ds { _env.rm(), _fb.dataspace() }; + + typedef Pixel_rgb565 PT; + + Surface_base::Area const _size { (unsigned)_fb.mode().width(), + (unsigned)_fb.mode().height() }; + + Surface _surface { _fb_ds.local_addr(), _size }; + + char _glyph_buffer_array[8*1024]; + + Tff_font::Glyph_buffer _glyph_buffer { _glyph_buffer_array, sizeof(_glyph_buffer_array) }; + + Tff_font _font_1 { _binary_droidsansb10_tff_start, _glyph_buffer }; + Tff_font _font_2 { _binary_default_tff_start, _glyph_buffer }; + + Attached_rom_dataspace _vera_ttf_ds { _env, "Vera.ttf" }; + + Heap _heap { _env.ram(), _env.rm() }; + + Ttf_font _font_3 { _heap, _vera_ttf_ds.local_addr(), 13 }; + + Attached_rom_dataspace _config { _env, "config" }; + + Root_directory _root { _env, _heap, _config.xml().sub_node("vfs") }; + + Vfs_font _font_4 { _heap, _root, "fonts/regular" }; + + void _refresh() { _fb.refresh(0, 0, _size.w(), _size.h()); } + + Main(Env &env) : _env(env) + { + /* test positioning of text */ + _surface.clip(Rect(Point(0, 0), _size)); + Box_painter::paint(_surface, Rect(Point(200, 10), Area(250, 50)), Color(0, 100, 0)); + Text_painter::paint(_surface, + Text_painter::Position(200, 10), _font_1, + Color(255, 255, 255), + "Text aligned at the top-left corner"); + + Box_painter::paint(_surface, Rect(Point(200, 100), Area(250, 50)), Color(0, 100, 0)); + Text_painter::paint(_surface, + Text_painter::Position(210, (int)(100 - _font_1.baseline())), _font_1, + Color(255, 255, 255), + "Baseline of text aligned at the top"); + + /* test horizontal clipping boundaries */ + _surface.clip(Rect(Point(20, 15), Area(40, 300))); + Box_painter::paint(_surface, Rect(Point(0, 0), _size), Color(150, 20, 10)); + + for (int x = 0, y = -30; y < (int)_size.h() + 30; x++, y += _font_2.bounding_box().h()) + Text_painter::paint(_surface, + Text_painter::Position(x, y), _font_2, + Color(255, 255, 255), + "Text painter at work"); + + /* test horizontal subpixel positioning */ + _surface.clip(Rect(Point(90, 15), Area(100, 300))); + Box_painter::paint(_surface, Rect(Point(0, 0), _size), Color(150, 20, 10)); + + for (float x = 90, y = -30; y < (int)_size.h() + 30; x += 0.2, y += _font_3.bounding_box().h()) + Text_painter::paint(_surface, + Text_painter::Position(x, y), _font_3, + Color(255, 255, 255), + "This is a real textSub-=_HT-+=%@pixel positioning"); + + _surface.clip(Rect(Point(90, 320), Area(100, 300))); + Box_painter::paint(_surface, Rect(Point(0, 0), _size), Color(255, 255, 255)); + + for (float x = 90, y = 300; y < (int)_size.h() + 30; x += 0.2, y += _font_3.bounding_box().h()) + Text_painter::paint(_surface, + Text_painter::Position(x, y), _font_3, + Color(0, 0, 0), + "This is a real textSub-=_HT-+=%@pixel positioning"); + _refresh(); + + struct Lut + { + unsigned char value[256]; + + Lut() + { + auto fill_segment = [&] (long x1, long y1, long x2, long) + { + for (long i = x1>>8; i < x2>>8; i++) value[i] = min(255, y1>>8); + }; + + bezier(0, 0, 0, 130<<8, 256<<8, 260<<8, fill_segment, 7); + } + }; + + static Lut const lut; + _surface.clip(Rect(Point(0, 0), _size)); + + for (unsigned x = 0; x < 256; x++) + Box_painter::paint(_surface, + Rect(Point(x + 512, 280 - lut.value[x]), Area(1, 1)), + Color(255, 255, 255)); + _refresh(); + + _surface.clip(Rect(Point(0, 0), _size)); + char const *vfs_text_string = "Glyphs obtained from VFS"; + { + Timer::Connection timer(_env); + + unsigned long const start_us = timer.elapsed_us(); + + enum { ITERATIONS = 40 }; + for (int i = 0; i < ITERATIONS; i++) + Text_painter::paint(_surface, + Text_painter::Position(260 + (i*133 % 500), + 320 + (i*87 % 400)), + _font_4, Color(150 + i*73, 0, 200), + "Glyphs obtained from VFS"); + + unsigned long const end_us = timer.elapsed_us(); + unsigned long num_glyphs = strlen(vfs_text_string)*ITERATIONS; + + log("uncached painting: ", (float)(end_us - start_us)/num_glyphs, " us/glyph"); + _refresh(); + } + + for (size_t limit_kib = 32; limit_kib < 192; limit_kib += 16) + { + Cached_font cached_font(_heap, _font_4, Cached_font::Limit{limit_kib*1024}); + + Timer::Connection timer(_env); + + unsigned long const start_us = timer.elapsed_us(); + + /* use less iterations for small cache sizes */ + int const iterations = (limit_kib < 100) ? 200 : 2000; + for (int i = 0; i < iterations; i++) + Text_painter::paint(_surface, + Text_painter::Position(260 + (i*83 % 500), + 320 + (i*153 % 400)), + cached_font, Color(30, limit_kib, 150 + i*73), + "Glyphs obtained from VFS"); + + unsigned long const end_us = timer.elapsed_us(); + unsigned long num_glyphs = strlen(vfs_text_string)*iterations; + + log("cached painting: ", (float)(end_us - start_us)/num_glyphs, " us/glyph" + " (", cached_font.stats(), ")"); + _refresh(); + } + } +}; + + +void Component::construct(Genode::Env &env) +{ + /* + * The indirect dependency from libc (via ttf_font) introduces a global + * constructor in the binary. (typeinfo for 'Genode::Exception'). + * + * XXX Why is this ctors entry generated? + */ + env.exec_static_constructors(); + + static Test::Main main(env); +} + + +/* + * Resolve symbol required by libc. It is unused as we implement + * 'Component::construct' directly instead of initializing the libc. + */ + +#include + +void Libc::Component::construct(Libc::Env &) { } + diff --git a/repos/gems/src/test/text_painter/target.mk b/repos/gems/src/test/text_painter/target.mk new file mode 100644 index 0000000000..3c8bee85a2 --- /dev/null +++ b/repos/gems/src/test/text_painter/target.mk @@ -0,0 +1,8 @@ +TARGET = test-text_painter +SRC_CC = main.cc +LIBS = base ttf_font vfs + +SRC_BIN += droidsansb10.tff default.tff + +vpath %.tff $(call select_from_repositories,src/app/scout/data) +vpath %.tff $(call select_from_repositories,src/server/nitpicker)