326 lines
7.3 KiB
C++
326 lines
7.3 KiB
C++
/*
|
|
* \brief Generic interface of graphics backend and chunky template
|
|
* \date 2005-10-24
|
|
* \author Norman Feske <norman.feske@genode-labs.com>
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2005-2011 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.
|
|
*/
|
|
|
|
#ifndef _CANVAS_H_
|
|
#define _CANVAS_H_
|
|
|
|
#include "color.h"
|
|
#include "font.h"
|
|
|
|
|
|
class Canvas
|
|
{
|
|
protected:
|
|
|
|
int _clip_x1, _clip_y1; /* left-top of clipping area */
|
|
int _clip_x2, _clip_y2; /* right-bottom of clipping area */
|
|
|
|
int _w, _h; /* current size of canvas */
|
|
int _capacity; /* max number of pixels */
|
|
|
|
public:
|
|
|
|
virtual ~Canvas() { }
|
|
|
|
/**
|
|
* Define clipping rectangle
|
|
*/
|
|
void clip(int x, int y, int w, int h)
|
|
{
|
|
/* calculate left-top and right-bottom points of clipping rectangle */
|
|
_clip_x1 = x;
|
|
_clip_y1 = y;
|
|
_clip_x2 = x + w - 1;
|
|
_clip_y2 = y + h - 1;
|
|
|
|
/* check against canvas boundaries */
|
|
if (_clip_x1 < 0) _clip_x1 = 0;
|
|
if (_clip_y1 < 0) _clip_y1 = 0;
|
|
if (w > 0 && _clip_x2 > _w - 1) _clip_x2 = _w - 1;
|
|
if (h > 0 && _clip_y2 > _h - 1) _clip_y2 = _h - 1;
|
|
}
|
|
|
|
/**
|
|
* Request clipping rectangle
|
|
*/
|
|
int clip_x1() { return _clip_x1; }
|
|
int clip_y1() { return _clip_y1; }
|
|
int clip_x2() { return _clip_x2; }
|
|
int clip_y2() { return _clip_y2; }
|
|
|
|
int w() { return _w; }
|
|
int h() { return _h; }
|
|
|
|
/**
|
|
* Set logical size of canvas
|
|
*/
|
|
int set_size(int w, int h)
|
|
{
|
|
if (w*h > _capacity) return -1;
|
|
_w = w;
|
|
_h = h;
|
|
clip(0, 0, w - 1, h - 1);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Draw filled box
|
|
*/
|
|
virtual void draw_box(int x, int y, int w, int h, Color c) = 0;
|
|
|
|
/**
|
|
* Draw string
|
|
*/
|
|
virtual void draw_string(int x, int y, Font *font, Color color, const char *str, int len) = 0;
|
|
|
|
/**
|
|
* Return base address
|
|
*/
|
|
virtual void *addr() = 0;
|
|
|
|
/**
|
|
* Define base address of pixel data
|
|
*/
|
|
virtual void addr(void *) = 0;
|
|
|
|
/**
|
|
* Anonymous texture struct
|
|
*/
|
|
class Texture {};
|
|
|
|
/**
|
|
* Allocate texture container
|
|
*/
|
|
virtual Texture *alloc_texture(int w, int h) = 0;
|
|
|
|
/**
|
|
* Free texture container
|
|
*/
|
|
virtual void free_texture(Texture *texture) = 0;
|
|
|
|
/**
|
|
* Assign rgba values to texture line
|
|
*/
|
|
virtual void set_rgba_texture(Texture *dst, unsigned char *rgba, int len, int y) = 0;
|
|
|
|
/**
|
|
* Draw texture
|
|
*/
|
|
virtual void draw_texture(Texture *src, int x, int y) { }
|
|
};
|
|
|
|
|
|
template <typename ST, int R_MASK, int R_SHIFT,
|
|
int G_MASK, int G_SHIFT,
|
|
int B_MASK, int B_SHIFT,
|
|
int A_MASK, int A_SHIFT>
|
|
class Pixel_rgba
|
|
{
|
|
private:
|
|
|
|
/**
|
|
* Shift left with positive or negative shift value
|
|
*/
|
|
inline int shift(int value, int shift)
|
|
{
|
|
return shift > 0 ? value << shift : value >> -shift;
|
|
}
|
|
|
|
public:
|
|
|
|
static const int r_mask = R_MASK, r_shift = R_SHIFT;
|
|
static const int g_mask = G_MASK, g_shift = G_SHIFT;
|
|
static const int b_mask = B_MASK, b_shift = B_SHIFT;
|
|
static const int a_mask = A_MASK, a_shift = A_SHIFT;
|
|
|
|
ST pixel;
|
|
|
|
/**
|
|
* Constructors
|
|
*/
|
|
Pixel_rgba() {}
|
|
|
|
Pixel_rgba(int red, int green, int blue, int alpha = 255)
|
|
{
|
|
rgba(red, green, blue, alpha);
|
|
}
|
|
|
|
/**
|
|
* Assign new rgba values
|
|
*/
|
|
void rgba(int red, int green, int blue, int alpha = 255)
|
|
{
|
|
pixel = (shift(red, r_shift) & r_mask)
|
|
| (shift(green, g_shift) & g_mask)
|
|
| (shift(blue, b_shift) & b_mask)
|
|
| (shift(alpha, a_shift) & a_mask);
|
|
}
|
|
|
|
inline int r() { return shift(pixel & r_mask, -r_shift); }
|
|
inline int g() { return shift(pixel & g_mask, -g_shift); }
|
|
inline int b() { return shift(pixel & b_mask, -b_shift); }
|
|
|
|
/**
|
|
* Multiply pixel with alpha value
|
|
*/
|
|
static inline Pixel_rgba blend(Pixel_rgba pixel, int alpha);
|
|
|
|
/**
|
|
* Mix two pixels at the ratio specified as alpha
|
|
*/
|
|
static inline Pixel_rgba mix(Pixel_rgba p1, Pixel_rgba p2, int alpha);
|
|
|
|
/**
|
|
* Compute average color value of two pixels
|
|
*/
|
|
static inline Pixel_rgba avr(Pixel_rgba p1, Pixel_rgba p2);
|
|
|
|
/**
|
|
* Compute average color value of four pixels
|
|
*/
|
|
static inline Pixel_rgba avr(Pixel_rgba p1, Pixel_rgba p2,
|
|
Pixel_rgba p3, Pixel_rgba p4)
|
|
{
|
|
return avr(avr(p1, p2), avr(p3, p4));
|
|
}
|
|
} __attribute__((packed));
|
|
|
|
|
|
template <typename PT>
|
|
class Chunky_canvas : public Canvas
|
|
{
|
|
protected:
|
|
|
|
PT *_addr; /* base address of pixel buffer */
|
|
|
|
/**
|
|
* Utilities
|
|
*/
|
|
static inline int min(int a, int b) { return a < b ? a : b; }
|
|
static inline int max(int a, int b) { return a > b ? a : b; }
|
|
|
|
public:
|
|
|
|
/**
|
|
* Initialize canvas
|
|
*/
|
|
void init(PT *addr, long capacity)
|
|
{
|
|
_addr = addr;
|
|
_capacity = capacity;
|
|
_w = _h = 0;
|
|
_clip_x1 = _clip_y1 = 0;
|
|
_clip_x2 = _clip_y2 = 0;
|
|
}
|
|
|
|
|
|
/****************************************
|
|
** Implementation of Canvas interface **
|
|
****************************************/
|
|
|
|
void draw_box(int x1, int y1, int w, int h, Color color)
|
|
{
|
|
int x2 = x1 + w - 1;
|
|
int y2 = y1 + h - 1;
|
|
|
|
/* check clipping */
|
|
if (x1 < _clip_x1) x1 = _clip_x1;
|
|
if (y1 < _clip_y1) y1 = _clip_y1;
|
|
if (x2 > _clip_x2) x2 = _clip_x2;
|
|
if (y2 > _clip_y2) y2 = _clip_y2;
|
|
|
|
if ((x1 > x2) || (y1 > y2)) return;
|
|
|
|
PT pix(color.r, color.g, color.b);
|
|
PT *dst, *dst_line = _addr + _w*y1 + x1;
|
|
|
|
int alpha = color.a;
|
|
|
|
/*
|
|
* ???
|
|
*
|
|
* Why can dst not be declared in the head of the inner for loop?
|
|
* Can I use the = operator for initializing a Pixel with a Color?
|
|
*
|
|
* ???
|
|
*/
|
|
|
|
if (alpha == Color::OPAQUE)
|
|
for (h = y2 - y1 + 1; h--; dst_line += _w)
|
|
for (dst = dst_line, w = x2 - x1 + 1; w--; dst++)
|
|
*dst = pix;
|
|
|
|
else if (alpha != Color::TRANSPARENT)
|
|
for (h = y2 - y1 + 1; h--; dst_line += _w)
|
|
for (dst = dst_line, w = x2 - x1 + 1; w--; dst++)
|
|
*dst = PT::mix(*dst, pix, alpha);
|
|
}
|
|
|
|
void draw_string(int x, int y, Font *font, Color color, const char *sstr, int len)
|
|
{
|
|
const unsigned char *str = (const unsigned char *)sstr;
|
|
|
|
if (!str || !font) return;
|
|
|
|
unsigned char *src = font->img;
|
|
int d, h = font->img_h;
|
|
|
|
/* check top clipping */
|
|
if ((d = _clip_y1 - y) > 0) {
|
|
src += d*font->img_w;
|
|
y += d;
|
|
h -= d;
|
|
}
|
|
|
|
/* check bottom clipping */
|
|
if ((d = y + h -1 - _clip_y2) > 0)
|
|
h -= d;
|
|
|
|
if (h < 1) return;
|
|
|
|
/* skip hidden glyphs */
|
|
for ( ; *str && len && (x + font->wtab[*str] < _clip_x1); len--)
|
|
x += font->wtab[*str++];
|
|
|
|
PT *dst = _addr + y*_w;
|
|
PT pix(color.r, color.g, color.b);
|
|
int alpha = color.a;
|
|
|
|
/* draw glyphs */
|
|
for ( ; *str && len && (x <= _clip_x2); str++, len--) {
|
|
|
|
int w = font->wtab[*str];
|
|
int start = max(0, _clip_x1 - x);
|
|
int end = min(w - 1, _clip_x2 - x);
|
|
PT *d = dst + x;
|
|
unsigned char *s = src + font->otab[*str];
|
|
|
|
for (int j = 0; j < h; j++, s += font->img_w, d += _w)
|
|
for (int i = start; i <= end; i++)
|
|
if (s[i]) d[i] = PT::mix(d[i], pix, (alpha*s[i]) >> 8);
|
|
|
|
x += w;
|
|
}
|
|
}
|
|
|
|
void *addr() { return _addr; }
|
|
void addr(void *addr) { _addr = static_cast<PT *>(addr); }
|
|
|
|
Texture *alloc_texture(int w, int h);
|
|
void free_texture(Texture *texture);
|
|
void set_rgba_texture(Texture *dst, unsigned char *rgba, int len, int y);
|
|
void draw_texture(Texture *src, int x, int y);
|
|
};
|
|
|
|
#endif /* _CANVAS_H_ */
|