// vi:ft=cpp /* * (c) 2010 Alexander Warg * economic rights: Technische Universität Dresden (Germany) * * This file is part of TUD:OS and distributed under the terms of the * GNU General Public License 2. * Please see the COPYING-GPL-2 file for details. */ #pragma once #include #include #include #include namespace Mag_gfx { namespace Mem { template< typename PT > class Canvas : public Mag_gfx::Canvas { public: typedef PT Pixel_traits; typedef typename PT::Pixel Pixel; typedef typename PT::Color Color; private: char *_pixels; Pixel_info const *_type; int _bpl; public: Canvas(void *pixels, Area const &size, unsigned bpl) : Mag_gfx::Canvas(size), _pixels((char *)pixels), _type(PT::type()), _bpl(bpl) {} Pixel_info const *type() const { return _type; } void *buffer() const { return _pixels; } void buffer(void *buf) { _pixels = (char *)buf; } int bytes_per_line() const { return _bpl; } private: template< typename CT > void _draw_box(char *dst_line, int _w, int h, CT color, int a); template< typename CT > void __draw_string(Point const &p, Font const *font, typename CT::Color color, char const *_str, unsigned len); bool _draw_alpha_texture(Texture const *texture, Pixel const *src, char *dst, int offset, int h, int w, int src_w); public: void draw_box(Rect const &rect, Rgba32::Color color) { Rect const clipped = _clip & rect; if (!clipped.valid() || color.a() == 0) return; char *dst_line = _pixels + _bpl*clipped.y1() + sizeof(Pixel)*clipped.x1(); if (color.a() >= 255) _draw_box(dst_line, clipped.w(), clipped.h(), Rgb32::Color(color.v()), 0); else _draw_box(dst_line, clipped.w(), clipped.h(), color, color.a()); flush_pixels(clipped); } void draw_string(Point const &p, Font const *font, Rgba32::Color color, char const *_str, unsigned len) { if (color.a() != Rgba32::Color::Amax) __draw_string(p, font, color, _str, len); else __draw_string(p, font, Rgb32::Color(color.v()), _str, len); } void draw_texture(Mag_gfx::Texture const *texture, Rgb32::Color mix_color, Point const &pos, Mix_mode mode); void draw_texture_scaled(Mag_gfx::Texture const *texture, Area const &size, Rgb32::Color mix_color, Point const &pos, Mix_mode mode); }; template< typename PT> template< typename CT > void Canvas::_draw_box(char *dst_line, int _w, int h, CT color, int a) { Color const c = color_conv(color); for (; h--; dst_line += _bpl) { int w; Pixel *dst = reinterpret_cast(dst_line); for (w = _w; w--; dst++) if (!CT::A::Size) *dst = c; else *dst = Color::Traits::mix(*dst, c, a); } } template< typename PT> template< typename CT > void Canvas::__draw_string(Point const &p, Font const *font, typename CT::Color color, char const *_str, unsigned len) { enum { Alphas = CT::A::Size }; const unsigned char *str = (const unsigned char *)_str; int x = p.x(), y = p.y(); if (!str || !font) return; unsigned char const *src = font->img; int d, h = font->str_h(_str); /* check top clipping */ if ((d = _clip.y1() - y) > 0) { src += d * font->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++]; int x_start = x; char *dst = _pixels + y * _bpl; Color pix = color_conv(color); int alpha = 255; if (Alphas) alpha = color.a() << (8 - Alphas); /* draw glyphs */ for ( ; *str && len && (x <= _clip.x2()); ++str, --len) { int w = font->wtab[*str]; int start = std::max(0, _clip.x1() - x); int end = std::min(w - 1, _clip.x2() - x); char *d = dst + x * sizeof(Pixel); unsigned char const *s = src + font->otab[*str]; for (int j = 0; j < h; ++j, s += font->w, d += _bpl) for (int i = start; i <= end; ++i) if (s[i]) { Pixel *p = reinterpret_cast(d) + i; *p = Pixel_traits::mix(*p, pix, (alpha * s[i]) >> 8); } x += w; } flush_pixels(Rect(Point(x_start, y), Area(x - x_start + 1, h))); } template bool Canvas::_draw_alpha_texture(Texture const *texture, Pixel const *src, char *dst, int offset, int h, int w, int src_w) { typedef typename Pixel_traits::Pixel Pixel; bool xa = !Pixel_traits::A::Size && texture->extra_alpha(); if (!xa && !Pixel_traits::A::Size) return false; unsigned char const *ab; if (xa) ab = texture->alpha_buffer() + offset; for (int j = h; j--; src += src_w, dst += _bpl) { Pixel *dp = reinterpret_cast(dst); Pixel const *s = src; unsigned char const *sab = ab; for (int i = w; i--; ++s, ++dp) { int alpha; if (!xa) alpha = Color(*s).a() << (8 - Pixel_traits::A::Size); else alpha = *sab++; if (alpha < 255) *dp = Pixel_traits::mix(*dp, *s, alpha); else if (alpha > 0) *dp = *s; } if (xa) ab += src_w; } return true; } template void Canvas::draw_texture(Mag_gfx::Texture const *texture, Rgb32::Color mix_color, Point const &pos, Mix_mode mode) { Rect const clipped = _clip & Rect(pos, texture->size()); if (!clipped.valid()) return; int src_w = texture->size().w(); Texture const *txt; txt = static_cast const *>(texture); Pixel const *src = txt->pixels() + (clipped.y1() - pos.y()) * src_w + clipped.x1() - pos.x(); char *dst = _pixels + clipped.y1() * _bpl + clipped.x1() * sizeof(Pixel); char *d; int i, j; Pixel const *s; Color mix_pixel = color_conv(mix_color); switch (mode) { case Alpha: if (_draw_alpha_texture(txt, src, dst, (clipped.y1() - pos.y()) * src_w + clipped.x1() - pos.x(), clipped.h(), clipped.w(), src_w)) break; // Fall through to solid! case Solid: // for (j = clipped.h(); j--; src += src_w, dst += _bpl) // for (i = clipped.w(), s = src, d = dst; i--; ++s, d += sizeof(Pixel)) // *reinterpret_cast(d) = *s; Blit::blit(src, src_w * sizeof(Pixel), dst, _bpl, clipped.w() * sizeof(Pixel), clipped.h()); break; case Mixed: mix_pixel = color_50(mix_pixel); for (j = clipped.h(); j--; src += src_w, dst += _bpl) for (i = clipped.w(), s = src, d = dst; i--; ++s, d += sizeof(Pixel)) *reinterpret_cast(d) = color_50(Color(*s)) + mix_pixel; break; case Masked: for (j = clipped.h(); j--; src += src_w, dst += _bpl) for (i = clipped.w(), s = src, d = dst; i--; ++s, d += sizeof(Pixel)) if (s->v()) *reinterpret_cast(d) = *s; break; } flush_pixels(clipped); } template< typename Pixel, typename Op > inline void draw_loop(Area const &area, unsigned const *cb, unsigned const *rb, unsigned bpl, char const *src, char *dst, Op const &op) { char const *s; char *d; int i, j; for (j = area.h(); j--; dst += bpl) for (i = area.w(), s = src + rb[j], d = dst; i--; d += sizeof(Pixel)) op(reinterpret_cast(d), reinterpret_cast(s + cb[i])); } namespace { static inline void calc_xscale_buffer(unsigned dx, unsigned dy, unsigned s, unsigned w, unsigned sbpp, unsigned *sb) { int f = dx / 2; unsigned y = 0; unsigned x = 0; for (x = 0; x < s; ++x) { f = f - dy; if (f < 0) { y += sbpp; f = f + dx; } } for (x = w; x--;) { f = f - dy; sb[x] = y; if (f < 0) { y += sbpp; f = f + dx; } } } static inline void calc_yscale_buffer(unsigned dx, unsigned dy, unsigned s, unsigned w, unsigned sbpp, unsigned *sb) { int f = dx / 2; unsigned y = 0; unsigned x = 0; unsigned xv = 0; for (x = 0; y < s && x < dx; ++x) { f = f - dy; if (f < 0) { f = f + dx; if (y >= s) break; ++y; } xv += sbpp; } y = w - 1; sb[y] = xv; if (!y) return; for (;x < dx; ++x) { f = f - dy; if (f < 0) { f = f + dx; --y; sb[y] = xv; if (y == 0) return; } xv += sbpp; } } template< typename Pixel > struct Solid_copy { void operator () (Pixel *d, Pixel const *s) const { *d = *s; } }; template< typename Pixel, typename Color > struct Mix_50_copy { Color mix_pixel; Mix_50_copy(Rgb32::Color mix_color) : mix_pixel(color_conv(mix_color)) {} void operator () (Pixel *d, Pixel const *s) const { *d = color_50(Color(*s)) + mix_pixel; } }; template< typename Pixel > struct Masked_copy { void operator () (Pixel *d, Pixel const *s) const { if (s->v()) *d = *s; } }; } template void Canvas::draw_texture_scaled(Mag_gfx::Texture const *texture, Area const &size, Rgb32::Color mix_color, Point const &pos, Mix_mode mode) { Rect const clipped = _clip & Rect(pos, size); //texture->size()); if (!clipped.valid()) return; Point tl_offs = clipped.p1() - pos; unsigned *col_buf = (unsigned*)alloca(sizeof(unsigned) * clipped.w()); unsigned *row_buf = (unsigned*)alloca(sizeof(unsigned) * clipped.h()); Area ts = texture->size(); if (size.w() > ts.w()) calc_xscale_buffer(size.w(), ts.w(), tl_offs.x(), clipped.w(), sizeof(Pixel), col_buf); else calc_yscale_buffer(ts.w(), size.w(), tl_offs.x(), clipped.w(), sizeof(Pixel), col_buf); if (size.h() > ts.h()) calc_xscale_buffer(size.h(), ts.h(), tl_offs.y(), clipped.h(), ts.w() * sizeof(Pixel), row_buf); else calc_yscale_buffer(ts.h(), size.h(), tl_offs.y(), clipped.h(), ts.w() * sizeof(Pixel), row_buf); char const *src = (char const *)texture->pixels(); char *dst = _pixels + clipped.y1() * _bpl + clipped.x1() * sizeof(Pixel); Mix_50_copy mix_copy(mix_color); switch (mode) { case Alpha: #if 0 if (_draw_alpha_texture(txt, src, dst, (clipped.y1() - pos.y()) * src_w + clipped.x1() - pos.x(), clipped.h(), clipped.w(), src_w)) break; #endif // Fall through to solid! case Solid: draw_loop(clipped.area(), col_buf, row_buf, _bpl, src, dst, Solid_copy()); break; case Mixed: draw_loop(clipped.area(), col_buf, row_buf, _bpl, src, dst, mix_copy); break; case Masked: draw_loop(clipped.area(), col_buf, row_buf, _bpl, src, dst, Masked_copy()); break; } flush_pixels(clipped); } }}