mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-31 00:24:51 +00:00
gems: painter for drawing sub-pixel accurate lines
This commit is contained in:
parent
ade05e5a7e
commit
e86758084c
184
repos/gems/include/polygon_gfx/line_painter.h
Normal file
184
repos/gems/include/polygon_gfx/line_painter.h
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* \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 <os/surface.h>
|
||||
#include <gems/bezier.h>
|
||||
|
||||
|
||||
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 y2)
|
||||
{
|
||||
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 <typename PT>
|
||||
static void _mix_pixel(PT &dst, PT src, int alpha)
|
||||
{
|
||||
dst = PT::mix(dst, src, alpha);
|
||||
}
|
||||
|
||||
template <typename PT>
|
||||
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 <typename PT>
|
||||
void paint(Genode::Surface<PT> &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 <typename PT>
|
||||
void paint(Genode::Surface<PT> &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_ */
|
Loading…
x
Reference in New Issue
Block a user