/* * \brief Functor for drawing lines on a surface * \author Norman Feske * \date 2017-07-27 */ /* * Copyright (C) 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. */ #ifndef _INCLUDE__POLYGON_GFX__LINE_PAINTER_H_ #define _INCLUDE__POLYGON_GFX__LINE_PAINTER_H_ #include #include struct Line_painter { typedef Genode::Surface_base::Rect Rect; typedef Genode::Surface_base::Point Point; /** * Fixpoint number with 8 fractional bits */ class Fixpoint { public: enum { FRAC_BITS = 8, FRAC_MASK = (1 << FRAC_BITS) - 1 }; long const value; private: Fixpoint(long v) : value(v) { } public: static Fixpoint from_int(long v) { return Fixpoint(v << FRAC_BITS); } static Fixpoint from_raw(long v) { return Fixpoint(v); } long integer() const { return value >> FRAC_BITS; } long fractional() const { return value & FRAC_MASK; } }; /** * Look-up table used for the non-linear application of alpha values */ struct Lut { unsigned char value[255]; Lut() { auto fill_segment = [&] (long x1, long y1, long x2, long) { for (long i = x1; i < x2; i++) value[i] = y1; }; int const v = 210; bezier(0, 0, 255 - v, v, 255, 255, fill_segment, 6); } }; /* * Use a single 'Lut' object across multiple instances of 'Line_painter'. */ static Lut const &_init_lut() { static Lut lut; return lut; } Lut const &_lut = _init_lut(); template static void _mix_pixel(PT &dst, PT src, int alpha) { dst = PT::mix(dst, src, alpha); } template void _transfer_pixel(PT *dst, unsigned dst_w, PT pixel, int alpha, Fixpoint x, Fixpoint y) const { dst += y.integer()*dst_w + x.integer(); /* * The values of 'u' and 'v' correspond to the subpixel position * of the coordinate (x, y) within its surrounding four pixels. * They are used to weight the transfer of the color value to * those pixels. */ unsigned long const u = x.fractional(), inv_u = 255 - u, v = y.fractional(), inv_v = 255 - v; _mix_pixel(dst[0], pixel, _lut.value[(alpha * inv_u * inv_v) >> 16]); _mix_pixel(dst[1], pixel, _lut.value[(alpha * u * inv_v) >> 16]); _mix_pixel(dst[dst_w], pixel, _lut.value[(alpha * inv_u * v) >> 16]); _mix_pixel(dst[dst_w + 1], pixel, _lut.value[(alpha * u * v) >> 16]); } /** * Draw line with sub-pixel accuracy * * The line is drawn only if both points reside within the clipping area of * the surface. * * Internally, the coordinates are interpolated as fixpoint values with a * fractional part of 16 bits. Therefore, the integer part of the * coordinate arguments must not exceed 16 bits (on 32-bit platforms where * 'long' has 32 bits). * * This function does not call 'surface.flush_pixels()' as it is meant * to draw curve segments at a fine granularity. In this case, only one * call of 'flush_pixels' per curve is preferred. */ template void paint(Genode::Surface &surface, Fixpoint x1, Fixpoint y1, Fixpoint x2, Fixpoint y2, Genode::Color color) const { using namespace Genode; /* * Reduce clipping area by one pixel as the antialiased line touches an * area of 2x2 for each pixel. */ Rect const clip(surface.clip().p1(), surface.clip().p2() + Point(-1, -1)); /* both points must reside within clipping area */ if (!surface.clip().contains(Point(x1.integer(), y1.integer())) || !surface.clip().contains(Point(x2.integer(), y2.integer()))) return; long const dx_f = x2.value - x1.value, dy_f = y2.value - y1.value; long const num_steps = max(abs(dx_f) + 127, abs(dy_f) + 127) >> 8; if (num_steps == 0) return; /* * For the interpolation of the coordinates, we use a 16 bits for the * fractional part (8 bit of the 'Fixpoint' value + 8 bit additional * precision. */ long const x_ascent = (dx_f << 8)/num_steps, y_ascent = (dy_f << 8)/num_steps; PT const pixel(color.r, color.g, color.b); int const alpha = color.a; PT * const dst = surface.addr(); unsigned const dst_w = surface.size().w(); long x = x1.value << 8, y = y1.value << 8; for (long i = 0; i < num_steps; i++) { _transfer_pixel(dst, dst_w, pixel, alpha, Fixpoint::from_raw(x >> 8), Fixpoint::from_raw(y >> 8)); x += x_ascent; y += y_ascent; } } template void paint(Genode::Surface &surface, Point p1, Point p2, Genode::Color color) const { paint(surface, Fixpoint::from_int(p1.x()), Fixpoint::from_int(p1.y()), Fixpoint::from_int(p2.x()), Fixpoint::from_int(p2.y()), color); surface.flush_pixels(Rect(p1, p2)); } }; #endif /* _INCLUDE__POLYGON_GFX__LINE_PAINTER_H_ */