-
Notifications
You must be signed in to change notification settings - Fork 5
/
Transparent.cpp
206 lines (178 loc) · 7.75 KB
/
Transparent.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#include <SFML/Graphics.hpp>
#if defined (SFML_SYSTEM_WINDOWS)
#include <windows.h>
bool setShape(HWND hWnd, const sf::Image& image)
{
const sf::Uint8* pixelData = image.getPixelsPtr();
// Create a region with the size of the entire window
HRGN hRegion = CreateRectRgn(0, 0, image.getSize().x, image.getSize().y);
// Loop over the pixels in the image and for each pixel where the alpha component equals 0,
// we will remove that pixel from the region.
// As an optimization, we will combine adjacent transparent pixels on the same row and
// remove them together, instead of using "CreateRectRgn(x, y, x+1, y+1)" to define
// a region for each transparent pixel individually.
bool transparentPixelFound = false;
unsigned int rectLeft = 0;
for (unsigned int y = 0; y < image.getSize().y; y++)
{
for (unsigned int x = 0; x < image.getSize().x; x++)
{
const bool isTransparentPixel = (pixelData[y * image.getSize().x * 4 + x * 4 + 3] == 0);
if (isTransparentPixel && !transparentPixelFound)
{
transparentPixelFound = true;
rectLeft = x;
}
else if (!isTransparentPixel && transparentPixelFound)
{
HRGN hRegionPixel = CreateRectRgn(rectLeft, y, x, y+1);
CombineRgn(hRegion, hRegion, hRegionPixel, RGN_XOR);
DeleteObject(hRegionPixel);
transparentPixelFound = false;
}
}
if (transparentPixelFound)
{
HRGN hRegionPixel = CreateRectRgn(rectLeft, y, image.getSize().x, y+1);
CombineRgn(hRegion, hRegion, hRegionPixel, RGN_XOR);
DeleteObject(hRegionPixel);
transparentPixelFound = false;
}
}
SetWindowRgn(hWnd, hRegion, true);
DeleteObject(hRegion);
return true;
}
bool setTransparency(HWND hWnd, unsigned char alpha)
{
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, 0, alpha, LWA_ALPHA);
return true;
}
#elif defined (SFML_SYSTEM_LINUX)
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
bool setShape(Window wnd, const sf::Image& image)
{
Display* display = XOpenDisplay(NULL);
// Setting the window shape requires the XShape extension
int event_base;
int error_base;
if (!XShapeQueryExtension(display, &event_base, &error_base))
{
XCloseDisplay(display);
return false;
}
const sf::Uint8* pixelData = image.getPixelsPtr();
// Create a black and white pixmap that has the size of the window
Pixmap pixmap = XCreatePixmap(display, wnd, image.getSize().x, image.getSize().y, 1);
GC gc = XCreateGC(display, pixmap, 0, NULL);
// Make the entire pixmap white
XSetForeground(display, gc, 1);
XFillRectangle(display, pixmap, gc, 0, 0, image.getSize().x, image.getSize().y);
// Loop over the pixels in the image and change the color of the pixmap to black
// for each pixel where the alpha component equals 0.
// As an optimization, we will combine adjacent transparent pixels on the same row and
// draw them together, instead of calling "XFillRectangle(display, pixmap, gc, x, y, 1, 1)"
// for each transparent pixel individually.
XSetForeground(display, gc, 0);
bool transparentPixelFound = false;
unsigned int rectLeft = 0;
for (unsigned int y = 0; y < image.getSize().y; y++)
{
for (unsigned int x = 0; x < image.getSize().x; x++)
{
const bool isTransparentPixel = (pixelData[y * image.getSize().x * 4 + x * 4 + 3] == 0);
if (isTransparentPixel && !transparentPixelFound)
{
transparentPixelFound = true;
rectLeft = x;
}
else if (!isTransparentPixel && transparentPixelFound)
{
XFillRectangle(display, pixmap, gc, rectLeft, y, x - rectLeft, 1);
transparentPixelFound = false;
}
}
if (transparentPixelFound)
{
XFillRectangle(display, pixmap, gc, rectLeft, y, image.getSize().x - rectLeft, 1);
transparentPixelFound = false;
}
}
// Use the black and white pixmap to define the shape of the window. All pixels that are
// white will be kept, while all black pixels will be clipped from the window.
XShapeCombineMask(display, wnd, ShapeBounding, 0, 0, pixmap, ShapeSet);
// Free resources
XFreeGC(display, gc);
XFreePixmap(display, pixmap);
XFlush(display);
XCloseDisplay(display);
return true;
}
bool setTransparency(Window wnd, unsigned char alpha)
{
Display* display = XOpenDisplay(NULL);
unsigned long opacity = (0xffffffff / 0xff) * alpha;
Atom property = XInternAtom(display, "_NET_WM_WINDOW_OPACITY", false);
if (property != None)
{
XChangeProperty(display, wnd, property, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&opacity, 1);
XFlush(display);
XCloseDisplay(display);
return true;
}
else
{
XCloseDisplay(display);
return false;
}
}
#undef None // None conflicts with SFML
#elif defined (SFML_SYSTEM_MACOS)
bool setShape(sf::WindowHandle handle, const sf::Image& image);
bool setTransparency(sf::WindowHandle handle, unsigned char alpha);
#else
bool setShape(sf::WindowHandle handle, const sf::Image& image)
{
return false;
}
bool setTransparency(sf::WindowHandle handle, unsigned char alpha)
{
return false;
}
#endif
int main()
{
// Change this to the wanted transparency
const unsigned char opacity = 185;
// Load an image with transparent parts that will define the shape of the window
sf::Image backgroundImage;
backgroundImage.loadFromFile("image.png");
// Create the window and center it on the screen
sf::RenderWindow window(sf::VideoMode(backgroundImage.getSize().x, backgroundImage.getSize().y, 32), "Transparent Window", sf::Style::None);
window.setPosition(sf::Vector2i((sf::VideoMode::getDesktopMode().width - backgroundImage.getSize().x) / 2,
(sf::VideoMode::getDesktopMode().height - backgroundImage.getSize().y) / 2));
// These functions return false on an unsupported OS or when it is not supported on linux (e.g. display doesn't support shape extention)
setShape(window.getSystemHandle(), backgroundImage);
setTransparency(window.getSystemHandle(), opacity);
// We will also draw the image on the window instead of just showing an empty window with the wanted shape
sf::Texture backgroundTexture;
sf::Sprite backgroundSprite;
backgroundTexture.loadFromImage(backgroundImage);
backgroundSprite.setTexture(backgroundTexture);
// Main loop to display the image while the window is open (pressing the escape key to close the window)
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape))
window.close();
}
window.clear(sf::Color::Transparent);
window.draw(backgroundSprite);
window.display();
}
return 0;
}