mirror of
https://github.com/RGBCube/serenity
synced 2025-07-28 00:37:35 +00:00
WindowServer: Support windows with alpha channels. And per-WSWindow opacity.
This patch also adds a Format concept to GraphicsBitmap. For now there are only two formats: RGB32 and RGBA32. Windows with alpha channel have their backing stores created in the RGBA32 format. Use this to make Terminal windows semi-transparent for that comfy rice look. There is one problem here, in that window compositing overdraw incurs multiple passes of blending of the same pixels. This leads to a mismatch in opacity which is obviously not good. I will work on this in a later patch. The alpha blending is currently straight C++. It should be relatively easy to optimize this using SSE instructions. For now I'm just happy with the cute effect. :^)
This commit is contained in:
parent
d4973842c9
commit
9b71307d49
25 changed files with 244 additions and 71 deletions
|
@ -23,5 +23,5 @@ Color::Color(NamedColor named)
|
|||
default: ASSERT_NOT_REACHED(); break;
|
||||
}
|
||||
|
||||
m_value = (rgb.r << 16) | (rgb.g << 8) | rgb.b;
|
||||
m_value = 0xff000000 | (rgb.r << 16) | (rgb.g << 8) | rgb.b;
|
||||
}
|
||||
|
|
|
@ -26,26 +26,48 @@ public:
|
|||
|
||||
Color() { }
|
||||
Color(NamedColor);
|
||||
Color(byte r, byte g, byte b) : m_value((r << 16) | (g << 8) | b) { }
|
||||
Color(RGBA32 rgba) : m_value(rgba) { }
|
||||
Color(byte r, byte g, byte b) : m_value(0xff000000 | (r << 16) | (g << 8) | b) { }
|
||||
Color(byte r, byte g, byte b, byte a) : m_value((a << 24) | (r << 16) | (g << 8) | b) { }
|
||||
|
||||
static Color from_rgb(unsigned rgb) { return Color(rgb | 0xff000000); }
|
||||
static Color from_rgba(unsigned rgba) { return Color(rgba); }
|
||||
|
||||
byte red() const { return (m_value >> 16) & 0xff; }
|
||||
byte green() const { return (m_value >> 8) & 0xff; }
|
||||
byte blue() const { return m_value & 0xff; }
|
||||
byte alpha() const { return (m_value >> 24) & 0xff; }
|
||||
|
||||
Color blend(Color source) const
|
||||
void set_alpha(byte value)
|
||||
{
|
||||
RGBA32 redblue1 = ((0x100u - source.alpha()) * (m_value & 0xff00ff)) >> 8;
|
||||
RGBA32 redblue2 = (source.alpha() * (source.m_value & 0xff00ff)) >> 8;
|
||||
RGBA32 green1 = ((0x100u - source.alpha()) * (m_value & 0x00ff00)) >> 8;
|
||||
RGBA32 green2 = (source.alpha() * (source.m_value & 0x00ff00)) >> 8;
|
||||
return Color(((redblue1 | redblue2) & 0xff00ff) + ((green1 | green2) & 0x00ff00));
|
||||
m_value &= 0x00ffffff;
|
||||
m_value |= value << 24;
|
||||
}
|
||||
|
||||
Color with_alpha(byte alpha)
|
||||
{
|
||||
return Color((m_value & 0x00ffffff) | alpha << 24);
|
||||
}
|
||||
|
||||
Color blend(Color source) const
|
||||
{
|
||||
if (!alpha() || source.alpha() == 255)
|
||||
return source;
|
||||
|
||||
if (!source.alpha())
|
||||
return *this;
|
||||
|
||||
int d = 255 * (alpha() + source.alpha()) - alpha() * source.alpha();
|
||||
byte r = (red() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.red()) / d;
|
||||
byte g = (green() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.green()) / d;
|
||||
byte b = (blue() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.blue()) / d;
|
||||
byte a = d / 255;
|
||||
return Color(r, g, b, a);
|
||||
}
|
||||
|
||||
RGBA32 value() const { return m_value; }
|
||||
|
||||
private:
|
||||
explicit Color(RGBA32 rgba) : m_value(rgba) { }
|
||||
|
||||
RGBA32 m_value { 0 };
|
||||
};
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::create(const Size& size)
|
||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::create(Format format, const Size& size)
|
||||
{
|
||||
return adopt(*new GraphicsBitmap(size));
|
||||
return adopt(*new GraphicsBitmap(format, size));
|
||||
}
|
||||
|
||||
GraphicsBitmap::GraphicsBitmap(const Size& size)
|
||||
GraphicsBitmap::GraphicsBitmap(Format format, const Size& size)
|
||||
: m_size(size)
|
||||
, m_pitch(size.width() * sizeof(RGBA32))
|
||||
, m_format(format)
|
||||
{
|
||||
size_t size_in_bytes = size.area() * sizeof(RGBA32);
|
||||
m_data = (RGBA32*)mmap(nullptr, size_in_bytes, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
|
@ -21,12 +22,12 @@ GraphicsBitmap::GraphicsBitmap(const Size& size)
|
|||
m_mmaped = true;
|
||||
}
|
||||
|
||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::create_wrapper(const Size& size, RGBA32* data)
|
||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::create_wrapper(Format format, const Size& size, RGBA32* data)
|
||||
{
|
||||
return adopt(*new GraphicsBitmap(size, data));
|
||||
return adopt(*new GraphicsBitmap(format, size, data));
|
||||
}
|
||||
|
||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::load_from_file(const String& path, const Size& size)
|
||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::load_from_file(Format format, const String& path, const Size& size)
|
||||
{
|
||||
int fd = open(path.characters(), O_RDONLY, 0644);
|
||||
if (fd < 0) {
|
||||
|
@ -44,19 +45,20 @@ RetainPtr<GraphicsBitmap> GraphicsBitmap::load_from_file(const String& path, con
|
|||
|
||||
int rc = close(fd);
|
||||
ASSERT(rc == 0);
|
||||
auto bitmap = create_wrapper(size, mapped_data);
|
||||
auto bitmap = create_wrapper(format, size, mapped_data);
|
||||
bitmap->m_mmaped = true;
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
GraphicsBitmap::GraphicsBitmap(const Size& size, RGBA32* data)
|
||||
GraphicsBitmap::GraphicsBitmap(Format format, const Size& size, RGBA32* data)
|
||||
: m_size(size)
|
||||
, m_data(data)
|
||||
, m_pitch(size.width() * sizeof(RGBA32))
|
||||
, m_format(format)
|
||||
{
|
||||
}
|
||||
|
||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::create_with_shared_buffer(int shared_buffer_id, const Size& size, RGBA32* data)
|
||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::create_with_shared_buffer(Format format, int shared_buffer_id, const Size& size, RGBA32* data)
|
||||
{
|
||||
if (!data) {
|
||||
void* shared_buffer = get_shared_buffer(shared_buffer_id);
|
||||
|
@ -64,13 +66,14 @@ RetainPtr<GraphicsBitmap> GraphicsBitmap::create_with_shared_buffer(int shared_b
|
|||
return nullptr;
|
||||
data = (RGBA32*)shared_buffer;
|
||||
}
|
||||
return adopt(*new GraphicsBitmap(shared_buffer_id, size, data));
|
||||
return adopt(*new GraphicsBitmap(format, shared_buffer_id, size, data));
|
||||
}
|
||||
|
||||
GraphicsBitmap::GraphicsBitmap(int shared_buffer_id, const Size& size, RGBA32* data)
|
||||
GraphicsBitmap::GraphicsBitmap(Format format, int shared_buffer_id, const Size& size, RGBA32* data)
|
||||
: m_size(size)
|
||||
, m_data(data)
|
||||
, m_pitch(size.width() * sizeof(RGBA32))
|
||||
, m_format(format)
|
||||
, m_shared_buffer_id(shared_buffer_id)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -9,10 +9,12 @@
|
|||
|
||||
class GraphicsBitmap : public Retainable<GraphicsBitmap> {
|
||||
public:
|
||||
static RetainPtr<GraphicsBitmap> create(const Size&);
|
||||
static RetainPtr<GraphicsBitmap> create_wrapper(const Size&, RGBA32*);
|
||||
static RetainPtr<GraphicsBitmap> load_from_file(const String& path, const Size&);
|
||||
static RetainPtr<GraphicsBitmap> create_with_shared_buffer(int shared_buffer_id, const Size&, RGBA32* buffer = nullptr);
|
||||
enum class Format { Invalid, RGB32, RGBA32 };
|
||||
|
||||
static RetainPtr<GraphicsBitmap> create(Format, const Size&);
|
||||
static RetainPtr<GraphicsBitmap> create_wrapper(Format, const Size&, RGBA32*);
|
||||
static RetainPtr<GraphicsBitmap> load_from_file(Format, const String& path, const Size&);
|
||||
static RetainPtr<GraphicsBitmap> create_with_shared_buffer(Format, int shared_buffer_id, const Size&, RGBA32* buffer = nullptr);
|
||||
~GraphicsBitmap();
|
||||
|
||||
RGBA32* scanline(int y);
|
||||
|
@ -25,14 +27,17 @@ public:
|
|||
size_t pitch() const { return m_pitch; }
|
||||
int shared_buffer_id() const { return m_shared_buffer_id; }
|
||||
|
||||
bool has_alpha_channel() const { return m_format == Format::RGBA32; }
|
||||
|
||||
private:
|
||||
GraphicsBitmap(const Size&);
|
||||
GraphicsBitmap(const Size&, RGBA32*);
|
||||
GraphicsBitmap(int shared_buffer_id, const Size&, RGBA32*);
|
||||
GraphicsBitmap(Format, const Size&);
|
||||
GraphicsBitmap(Format, const Size&, RGBA32*);
|
||||
GraphicsBitmap(Format, int shared_buffer_id, const Size&, RGBA32*);
|
||||
|
||||
Size m_size;
|
||||
RGBA32* m_data { nullptr };
|
||||
size_t m_pitch { 0 };
|
||||
Format m_format { Format::Invalid };
|
||||
bool m_mmaped { false };
|
||||
int m_shared_buffer_id { -1 };
|
||||
};
|
||||
|
|
|
@ -32,7 +32,10 @@ Painter::Painter(GWidget& widget)
|
|||
request.window_id = widget.window()->window_id();
|
||||
auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::DidGetWindowBackingStore);
|
||||
|
||||
m_target = GraphicsBitmap::create_with_shared_buffer(response.backing.shared_buffer_id, response.backing.size);
|
||||
m_target = GraphicsBitmap::create_with_shared_buffer(
|
||||
response.backing.has_alpha_channel ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32,
|
||||
response.backing.shared_buffer_id,
|
||||
response.backing.size);
|
||||
ASSERT(m_target);
|
||||
m_window = widget.window();
|
||||
m_translation.move_by(widget.window_relative_rect().location());
|
||||
|
@ -67,7 +70,7 @@ void Painter::fill_rect_with_draw_op(const Rect& a_rect, Color color)
|
|||
|
||||
for (int i = rect.height() - 1; i >= 0; --i) {
|
||||
for (int j = 0; j < rect.width(); ++j)
|
||||
set_pixel_with_draw_op(dst[j], color.value());
|
||||
set_pixel_with_draw_op(dst[j], color);
|
||||
dst += dst_skip;
|
||||
}
|
||||
}
|
||||
|
@ -230,8 +233,17 @@ void Painter::draw_bitmap(const Point& p, const GlyphBitmap& bitmap, Color color
|
|||
}
|
||||
}
|
||||
|
||||
void Painter::blit_with_alpha(const Point& position, const GraphicsBitmap& source, const Rect& src_rect)
|
||||
void Painter::blit_with_opacity(const Point& position, const GraphicsBitmap& source, const Rect& src_rect, float opacity)
|
||||
{
|
||||
ASSERT(!m_target->has_alpha_channel());
|
||||
|
||||
if (!opacity)
|
||||
return;
|
||||
if (opacity >= 1.0f)
|
||||
return blit(position, source, src_rect);
|
||||
|
||||
byte alpha = 255 * opacity;
|
||||
|
||||
Rect dst_rect(position, src_rect.size());
|
||||
dst_rect.move_by(m_translation);
|
||||
auto clipped_rect = Rect::intersection(dst_rect, m_clip_rect);
|
||||
|
@ -248,13 +260,42 @@ void Painter::blit_with_alpha(const Point& position, const GraphicsBitmap& sourc
|
|||
|
||||
for (int row = first_row; row <= last_row; ++row) {
|
||||
for (int x = 0; x <= (last_column - first_column); ++x) {
|
||||
byte alpha = Color(src[x]).alpha();
|
||||
Color src_color_with_alpha = Color::from_rgb(src[x]);
|
||||
src_color_with_alpha.set_alpha(alpha);
|
||||
Color dst_color = Color::from_rgb(dst[x]);
|
||||
dst[x] = dst_color.blend(src_color_with_alpha).value();
|
||||
}
|
||||
dst += dst_skip;
|
||||
src += src_skip;
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::blit_with_alpha(const Point& position, const GraphicsBitmap& source, const Rect& src_rect)
|
||||
{
|
||||
ASSERT(source.has_alpha_channel());
|
||||
Rect dst_rect(position, src_rect.size());
|
||||
dst_rect.move_by(m_translation);
|
||||
auto clipped_rect = Rect::intersection(dst_rect, m_clip_rect);
|
||||
if (clipped_rect.is_empty())
|
||||
return;
|
||||
const int first_row = clipped_rect.top() - dst_rect.top();
|
||||
const int last_row = clipped_rect.bottom() - dst_rect.top();
|
||||
const int first_column = clipped_rect.left() - dst_rect.left();
|
||||
const int last_column = clipped_rect.right() - dst_rect.left();
|
||||
RGBA32* dst = m_target->scanline(clipped_rect.y()) + clipped_rect.x();
|
||||
const RGBA32* src = source.scanline(src_rect.top() + first_row) + src_rect.left() + first_column;
|
||||
const size_t dst_skip = m_target->width();
|
||||
const unsigned src_skip = source.width();
|
||||
|
||||
for (int row = first_row; row <= last_row; ++row) {
|
||||
for (int x = 0; x <= (last_column - first_column); ++x) {
|
||||
byte alpha = Color::from_rgba(src[x]).alpha();
|
||||
if (alpha == 0xff)
|
||||
dst[x] = src[x];
|
||||
else if (!alpha)
|
||||
continue;
|
||||
else
|
||||
dst[x] = Color(dst[x]).blend(src[x]).value();
|
||||
dst[x] = Color::from_rgba(dst[x]).blend(Color::from_rgba(src[x])).value();
|
||||
}
|
||||
dst += dst_skip;
|
||||
src += src_skip;
|
||||
|
@ -263,6 +304,8 @@ void Painter::blit_with_alpha(const Point& position, const GraphicsBitmap& sourc
|
|||
|
||||
void Painter::blit(const Point& position, const GraphicsBitmap& source, const Rect& src_rect)
|
||||
{
|
||||
if (source.has_alpha_channel())
|
||||
return blit_with_alpha(position, source, src_rect);
|
||||
Rect dst_rect(position, src_rect.size());
|
||||
dst_rect.move_by(m_translation);
|
||||
auto clipped_rect = Rect::intersection(dst_rect, m_clip_rect);
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
void draw_line(const Point&, const Point&, Color);
|
||||
void draw_focus_rect(const Rect&);
|
||||
void blit(const Point&, const GraphicsBitmap&, const Rect& src_rect);
|
||||
void blit_with_alpha(const Point&, const GraphicsBitmap&, const Rect& src_rect);
|
||||
void blit_with_opacity(const Point&, const GraphicsBitmap&, const Rect& src_rect, float opacity);
|
||||
|
||||
void draw_text(const Rect&, const String&, TextAlignment = TextAlignment::TopLeft, Color = Color());
|
||||
void draw_glyph(const Point&, char, Color);
|
||||
|
@ -58,6 +58,7 @@ public:
|
|||
private:
|
||||
void set_pixel_with_draw_op(dword& pixel, const Color&);
|
||||
void fill_rect_with_draw_op(const Rect&, Color);
|
||||
void blit_with_alpha(const Point&, const GraphicsBitmap&, const Rect& src_rect);
|
||||
|
||||
const Font* m_font;
|
||||
Point m_translation;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue