added transparency, cleanup
This commit is contained in:
2
Makefile
2
Makefile
@@ -42,7 +42,7 @@ ifeq ($(INSTALL),osx)
|
||||
SHADER_DIR = /Library/glava
|
||||
endif
|
||||
|
||||
LDFLAGS = -lpulse -lpulse-simple -pthread -lglfw -ldl -lm -lX11
|
||||
LDFLAGS = -lpulse -lpulse-simple -pthread -lglfw -ldl -lm -lX11 -lXext
|
||||
|
||||
PYTHON = python
|
||||
|
||||
|
||||
17
README.md
17
README.md
@@ -15,13 +15,11 @@ $ glava
|
||||
|
||||
You can pass `BUILD=debug` to the makefile for debug builds of both glad and glava, and you can manually specify install targets with `INSTALL=...`, possible arguments are `unix` for FHS compliant Linux and BSD distros, `osx` for Mac OSX, and `standalone` which allows you to run GLava in the build directory.
|
||||
|
||||
**Note:** GLFW's most recent stable version is 3.2, whereas 3.3 is needed in order to support transparency (older versions still work, just without transparency). You can either find a more recent build or compile it yourself. Arch users can install `glfw-x11-git` from the AUR.
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- X11
|
||||
- PulseAudio
|
||||
- GLFW (version 3.3+ needed for transparency support)
|
||||
- GLFW
|
||||
- Linux or BSD
|
||||
|
||||
**Additional compile time requirements:**
|
||||
@@ -40,6 +38,8 @@ To embed GLava in your desktop (for EWMH compliant window managers), use `#reque
|
||||
|
||||
\* On an XDG compliant Linux or BSD system. OSX will use `/Library/glava` and `~/Library/Preferences/glava` instead.
|
||||
|
||||
**Note for `#request setopacity`:** While most users will prefer the faster `xroot` transparency, GLFW 3.3 (unreleased) is needed in order to support the `native` transparency option (older versions still work). Arch users can install `glfw-x11-git` from the AUR and recompile GLava for this feature.
|
||||
|
||||
## To-Do
|
||||
|
||||
**What needs to be done:**
|
||||
@@ -55,10 +55,7 @@ To embed GLava in your desktop (for EWMH compliant window managers), use `#reque
|
||||
- Detecting if the window manager reports the currently focused window as fullscreen (and halting rendering for the duration)
|
||||
- Fixed a memory corruption bug that crashed the `nvidia` driver on Linux
|
||||
- Fix breaks in audio spectrum read from the PulseAudio server
|
||||
|
||||
**What will never be done:**
|
||||
|
||||
- Port to Windows (???)
|
||||
- Transparency support using either the compositor or the root X window's pixmap
|
||||
|
||||
## Licensing
|
||||
|
||||
@@ -79,4 +76,8 @@ The below copyright applies for the modifications to the files listed above, and
|
||||
|
||||
`Copyright (c) 2017 Levi Webb`
|
||||
|
||||
**Why did you relicense this software?** Because my views align with the FSF's position on free software -- I see the MIT license as harmful to free software as it fails to preserve modifications of the source code as open source.
|
||||
**Why did you relicense this software?** Because my views align with the FSF's position on free software -- I see the MIT license as harmful to free software as it fails to preserve modifications of the source code as open source.
|
||||
|
||||
## Porting
|
||||
|
||||
GLava was built with GLFW, making the graphics frontend mostly compatible if it were to be ported to Windows, and I have taken all the Xlib-specific code and placed it into `xwin.c` if anyone decides they wish to attempt at a port.
|
||||
|
||||
162
render.c
162
render.c
@@ -355,14 +355,14 @@ static void drawoverlay(const struct overlay_data* d) {
|
||||
struct gl_data {
|
||||
struct gl_sfbo* stages;
|
||||
struct overlay_data overlay;
|
||||
GLuint audio_tex_r, audio_tex_l;
|
||||
GLuint audio_tex_r, audio_tex_l, bg_tex;
|
||||
size_t stages_sz, bufscale, avg_frames;
|
||||
GLFWwindow* w;
|
||||
int lww, lwh; /* last window height */
|
||||
int lww, lwh, lwx, lwy; /* last window dimensions */
|
||||
int rate; /* framerate */
|
||||
double tcounter;
|
||||
int fcounter, ucounter, kcounter;
|
||||
bool print_fps, avg_window, interpolate, force_geometry;
|
||||
bool print_fps, avg_window, interpolate, force_geometry, copy_desktop;
|
||||
void** t_data;
|
||||
float gravity_step, target_spu, fr, ur, smooth_distance, smooth_ratio;
|
||||
float* interpolate_buf[6];
|
||||
@@ -660,6 +660,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
|
||||
struct gl_data* gl = r->gl;
|
||||
*gl = (struct gl_data) {
|
||||
.w = NULL,
|
||||
.stages = NULL,
|
||||
.rate = 0,
|
||||
.tcounter = 0.0D,
|
||||
@@ -676,7 +677,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
.interpolate = true,
|
||||
.force_geometry = false,
|
||||
.smooth_distance = 0.01,
|
||||
.smooth_ratio = 4
|
||||
.smooth_ratio = 4,
|
||||
.bg_tex = 0,
|
||||
.copy_desktop = true,
|
||||
.geometry = { 0, 0, 500, 400 }
|
||||
};
|
||||
|
||||
#ifdef GLAD_DEBUG
|
||||
@@ -695,40 +699,10 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
|
||||
#ifdef GLFW_TRANSPARENT_FRAMEBUFFER
|
||||
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
|
||||
#elif GLFW_TRANSPARENT
|
||||
glfwWindowHint(GLFW_TRANSPARENT, GLFW_TRUE);
|
||||
#else
|
||||
printf("WARNING: the linked version of GLFW3 does not have transparency support"
|
||||
" (GLFW_TRANSPARENT[_FRAMEBUFFER])!\n");
|
||||
#endif
|
||||
|
||||
if (!(gl->w = glfwCreateWindow(500, 400, "GLava", NULL, NULL))) {
|
||||
glfwTerminate();
|
||||
abort();
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent(gl->w);
|
||||
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
|
||||
int shader_version = 330;
|
||||
const char* module = force_mod;
|
||||
char* xwintype = NULL;
|
||||
char* xwintype = NULL, * wintitle = "GLava";
|
||||
bool loading_module = true;
|
||||
struct gl_sfbo* current = NULL;
|
||||
size_t t_count = 0;
|
||||
@@ -739,9 +713,30 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
|
||||
struct request_handler handlers[] = {
|
||||
{
|
||||
.name = "wavetype", .fmt = "s",
|
||||
.name = "setopacity", .fmt = "s",
|
||||
.handler = RHANDLER(name, args, {
|
||||
printf("[STUB] wavetype = '%s'\n", (char*) args[0]);
|
||||
|
||||
bool native_opacity = !strcmp("native", (char*) args[0]);
|
||||
|
||||
#ifdef GLFW_TRANSPARENT_FRAMEBUFFER
|
||||
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, native_opacity ? GLFW_TRUE : GLFW_FALSE);
|
||||
#elif GLFW_TRANSPARENT
|
||||
glfwWindowHint(GLFW_TRANSPARENT, native_opacity ? GLFW_TRUE : GLFW_FALSE);
|
||||
#else
|
||||
if (native_opacity)
|
||||
printf("WARNING: the linked version of GLFW3 does not have transparency support"
|
||||
" (GLFW_TRANSPARENT[_FRAMEBUFFER])!\n");
|
||||
#endif
|
||||
|
||||
if (!strcmp("xroot", (char*) args[0]))
|
||||
gl->copy_desktop = true;
|
||||
else
|
||||
gl->copy_desktop = false;
|
||||
|
||||
if (!gl->copy_desktop && !native_opacity && strcmp("none", (char*) args[0])) {
|
||||
fprintf(stderr, "Invalid opacity option: '%s'\n", (char*) args[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -773,8 +768,6 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
gl->geometry[1] = *(int*) args[1];
|
||||
gl->geometry[2] = *(int*) args[2];
|
||||
gl->geometry[3] = *(int*) args[3];
|
||||
glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]);
|
||||
glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]);
|
||||
})
|
||||
},
|
||||
{ .name = "setsource", .fmt = "s",
|
||||
@@ -793,7 +786,7 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
{ .name = "setprintframes", .fmt = "b",
|
||||
.handler = RHANDLER(name, args, { gl->print_fps = *(bool*) args[0]; }) },
|
||||
{ .name = "settitle", .fmt = "s",
|
||||
.handler = RHANDLER(name, args, { glfwSetWindowTitle(gl->w, (char*) args[0]); }) },
|
||||
.handler = RHANDLER(name, args, { wintitle = strdup((char*) args[0]); }) },
|
||||
{ .name = "setbufsize", .fmt = "i",
|
||||
.handler = RHANDLER(name, args, { r->bufsize_request = *(int*) args[0]; }) },
|
||||
{ .name = "setbufscale", .fmt = "i",
|
||||
@@ -938,6 +931,29 @@ struct renderer* rd_new(const char** paths, const char* entry, const char* force
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!(gl->w = glfwCreateWindow(500, 400, wintitle, NULL, NULL))) {
|
||||
glfwTerminate();
|
||||
abort();
|
||||
}
|
||||
|
||||
glfwSetWindowPos(gl->w, gl->geometry[0], gl->geometry[1]);
|
||||
glfwSetWindowSize(gl->w, gl->geometry[2], gl->geometry[3]);
|
||||
|
||||
glfwMakeContextCurrent(gl->w);
|
||||
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_DEPTH_CLAMP);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
size_t m_len = strlen(module);
|
||||
size_t bsz = d_len + m_len + 2;
|
||||
char shaders[bsz]; /* module pack path to use */
|
||||
@@ -1146,20 +1162,29 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
irb[t] = irbs + ((irbe - irbs) * mod);
|
||||
}
|
||||
}
|
||||
int ww, wh, wx, wy;
|
||||
glfwGetFramebufferSize(gl->w, &ww, &wh);
|
||||
glfwGetWindowPos(gl->w, &wx, &wy);
|
||||
|
||||
/* Resize screen textures if needed */
|
||||
int ww, wh;
|
||||
glfwGetFramebufferSize(gl->w, &ww, &wh);
|
||||
if (ww != gl->lww || wh != gl->lwh) {
|
||||
for (t = 0; t < gl->stages_sz; ++t) {
|
||||
if (gl->stages[t].valid) {
|
||||
setup_sfbo(&gl->stages[t], ww, wh);
|
||||
}
|
||||
}
|
||||
gl->lww = ww;
|
||||
gl->lwh = wh;
|
||||
}
|
||||
|
||||
|
||||
/* Resize and grab new background data if needed */
|
||||
if (gl->copy_desktop && (ww != gl->lww || wh != gl->lwh || wx != gl->lwx || wy != gl->lwy)) {
|
||||
gl->bg_tex = xwin_copyglbg(r, gl->bg_tex);
|
||||
}
|
||||
|
||||
gl->lwx = wx;
|
||||
gl->lwy = wy;
|
||||
gl->lww = ww;
|
||||
gl->lwh = wh;
|
||||
|
||||
glViewport(0, 0, ww, wh);
|
||||
|
||||
struct gl_sfbo* prev;
|
||||
@@ -1170,15 +1195,56 @@ void rd_update(struct renderer* r, float* lb, float* rb, size_t bsz, bool modifi
|
||||
|
||||
bool needed[64] = { [ 0 ... 63 ] = false }; /* Load flags for each texture position */
|
||||
|
||||
/* Select the program associated with this pass */
|
||||
/* Current shader program */
|
||||
struct gl_sfbo* current = &gl->stages[t];
|
||||
glUseProgram(current->shader);
|
||||
|
||||
|
||||
/* Bind framebuffer if this is not the final pass */
|
||||
if (current->valid)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (!current->valid && gl->copy_desktop) {
|
||||
/* Self-contained code for drawing background image */
|
||||
static GLuint bg_prog, bg_utex, bg_screen;
|
||||
static bool setup = false;
|
||||
/* Shader to flip texture and override alpha channel */
|
||||
static const char* frag_shader =
|
||||
"uniform sampler2D tex;" "\n"
|
||||
"uniform ivec2 screen;" "\n"
|
||||
"out vec4 fragment;" "\n"
|
||||
"in vec4 gl_FragCoord;" "\n"
|
||||
"void main() {" "\n"
|
||||
" fragment = texture(tex, vec2(gl_FragCoord.x / screen.x, " "\n"
|
||||
" (screen.y - gl_FragCoord.y) / screen.y));" "\n"
|
||||
" fragment.a = 1.0F;" "\n"
|
||||
"}"; "\n";
|
||||
if (!setup) {
|
||||
bg_prog = shaderlink(shaderload(NULL, GL_VERTEX_SHADER, VERTEX_SHADER_SRC,
|
||||
NULL, NULL, 330, true),
|
||||
shaderload(NULL, GL_FRAGMENT_SHADER, frag_shader,
|
||||
NULL, NULL, 330, true));
|
||||
bg_utex = glGetUniformLocation(bg_prog, "tex");
|
||||
bg_screen = glGetUniformLocation(bg_prog, "screen");
|
||||
glBindFragDataLocation(bg_prog, 1, "fragment");
|
||||
setup = true;
|
||||
}
|
||||
glUseProgram(bg_prog);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, gl->bg_tex);
|
||||
glUniform2i(bg_screen, (GLint) ww, (GLint) wh);
|
||||
glUniform1i(bg_utex, 0);
|
||||
/* We need to disable blending, we might read in bogus alpha values due
|
||||
to how we obtain the background texture (format is four byte `rgb_`,
|
||||
where the last value is skipped) */
|
||||
glDisable(GL_BLEND);
|
||||
drawoverlay(&gl->overlay);
|
||||
glEnable(GL_BLEND);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
/* Select the program associated with this pass */
|
||||
glUseProgram(current->shader);
|
||||
|
||||
bool prev_bound = false;
|
||||
|
||||
|
||||
@@ -59,22 +59,22 @@ void main() {
|
||||
d -= C_RADIUS + (float(C_LINE) / 2.0F); /* offset to fragment distance from inner circle */
|
||||
if (d <= v - BAR_OUTLINE_WIDTH) {
|
||||
#if BAR_OUTLINE_WIDTH > 0
|
||||
if (abs(ym) < (BAR_WIDTH / 2) - BAR_OUTLINE_WIDTH)
|
||||
if (abs(ym) < (BAR_WIDTH / 2) - BAR_OUTLINE_WIDTH)
|
||||
fragment = COLOR;
|
||||
else
|
||||
fragment = BAR_OUTLINE;
|
||||
#else
|
||||
fragment = COLOR;
|
||||
else
|
||||
fragment = BAR_OUTLINE;
|
||||
#else
|
||||
fragment = COLOR;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#if BAR_OUTLINE_WIDTH > 0
|
||||
if (d <= v) {
|
||||
fragment = BAR_OUTLINE;
|
||||
return;
|
||||
}
|
||||
fragment = BAR_OUTLINE;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
fragment = vec4(0, 0, 0, 0); /* default frag color */
|
||||
}
|
||||
}
|
||||
fragment = vec4(0, 0, 0, 0); /* default frag color */
|
||||
}
|
||||
|
||||
@@ -21,17 +21,38 @@
|
||||
#request setfocused true
|
||||
#request setmaximized false
|
||||
|
||||
/* Force GLFW window geometry (locking the window in place),
|
||||
useful for some pesky WMs that try to reposition the window
|
||||
when embedding in the desktop. */
|
||||
#request setforcegeometry false
|
||||
|
||||
/* Set window background opacity mode. Possible values are:
|
||||
|
||||
"native" - True transparency provided by the compositor.
|
||||
Requires GLFW 3.3+ and an active compositor. Can
|
||||
reduce performance on some systems, and will not
|
||||
blend with the visualizer's alpha layer.
|
||||
|
||||
"xroot" - Maintain a copy of the root window's pixmap
|
||||
(usually the desktop background) to provide a
|
||||
pseudo-transparent effect. Useful when no compositor
|
||||
is available or native transparency isn't nessecary.
|
||||
Has very little performance impact.
|
||||
|
||||
"none" - Disable window opacity completely. */
|
||||
#request setopacity "xroot"
|
||||
|
||||
/* OpenGL context and GLSL shader versions, do not change unless
|
||||
you *absolutely* know what you are doing. */
|
||||
#request setversion 3 3
|
||||
#request setshaderversion 330
|
||||
|
||||
/* GLFW window title */
|
||||
#request settitle "GLava"
|
||||
|
||||
/* GLFW window geometry (x, y, width, height) */
|
||||
#request setgeometry 0 0 400 600
|
||||
|
||||
/* Force GLFW window geometry (locking the window in place), useful
|
||||
for some pesky WMs that try to reposition the window when
|
||||
embedding in the desktop. */
|
||||
#request setforcegeometry false
|
||||
|
||||
/* (X11 only) EWMH Window type. Possible values are:
|
||||
|
||||
"desktop", "dock", "toolbar", "menu",
|
||||
@@ -139,8 +160,3 @@
|
||||
the buffer. It is reccommended to use `setsamplerate` and
|
||||
`setsamplesize` to improve performance or accuracy instead. */
|
||||
#request setbufscale 1
|
||||
|
||||
/* OpenGL context and GLSL shader versions, do not change unless
|
||||
you _absolutely_ know what you are doing. */
|
||||
#request setversion 3 3
|
||||
#request setshaderversion 330
|
||||
|
||||
139
xwin.c
139
xwin.c
@@ -7,10 +7,15 @@
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
#define GLFW_EXPOSE_NATIVE_X11
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <GLFW/glfw3native.h>
|
||||
|
||||
@@ -77,3 +82,137 @@ void xwin_settype(struct renderer* rd, const char* type) {
|
||||
XChangeProperty(d, w, wtype, XA_ATOM, 32, PropModeReplace, (unsigned char*) &desk, 1);
|
||||
XCloseDisplay(d);
|
||||
}
|
||||
|
||||
static Pixmap get_pixmap(Display* d, Window w) {
|
||||
Pixmap p;
|
||||
Atom act_type;
|
||||
int act_format;
|
||||
unsigned long nitems, bytes_after;
|
||||
unsigned char *data = NULL;
|
||||
Atom id;
|
||||
|
||||
id = XInternAtom(d, "_XROOTPMAP_ID", False);
|
||||
|
||||
if (XGetWindowProperty(d, w, id, 0, 1, False, XA_PIXMAP,
|
||||
&act_type, &act_format, &nitems, &bytes_after,
|
||||
&data) == Success) {
|
||||
if (data) {
|
||||
p = *((Pixmap *) data);
|
||||
XFree(data);
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
unsigned int xwin_copyglbg(struct renderer* rd, unsigned int tex) {
|
||||
GLuint texture = (GLuint) tex;
|
||||
if (!texture)
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
GLFWwindow* gwin = (GLFWwindow*) rd_get_impl_window(rd);
|
||||
int x, y, w, h;
|
||||
glfwGetFramebufferSize(gwin, &w, &h);
|
||||
glfwGetWindowPos(gwin, &x, &y);
|
||||
XColor c;
|
||||
Display* d = XOpenDisplay(0);
|
||||
Pixmap p = get_pixmap(d, RootWindow(d, DefaultScreen(d)));
|
||||
|
||||
/* Obtain section of root pixmap using XShm */
|
||||
|
||||
XShmSegmentInfo shminfo;
|
||||
Visual* visual = DefaultVisual(d, DefaultScreen(d));
|
||||
XVisualInfo match = { .visualid = XVisualIDFromVisual(visual) };
|
||||
int nret;
|
||||
XVisualInfo* info = XGetVisualInfo(d, VisualIDMask, &match, &nret);
|
||||
XImage* image = XShmCreateImage(d, visual, info->depth, ZPixmap, NULL,
|
||||
&shminfo, (unsigned int) w, (unsigned int) h);
|
||||
shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height, IPC_CREAT | 0777);
|
||||
shminfo.shmaddr = image->data = shmat(shminfo.shmid, 0, 0);
|
||||
shminfo.readOnly = false;
|
||||
XShmAttach(d, &shminfo);
|
||||
XShmGetImage(d, p, image, x, y, AllPlanes);
|
||||
|
||||
/* Try to convert pixel bit depth to OpenGL storage format. The following formats\
|
||||
will need intermediate conversion before OpenGL can accept the data:
|
||||
|
||||
- 8-bit pixel formats (retro displays, low-bandwidth virtual displays)
|
||||
- 36-bit pixel formats (rare deep color displays) */
|
||||
|
||||
bool invalid = false, aligned = false;
|
||||
GLenum type;
|
||||
switch (image->bits_per_pixel) {
|
||||
case 16:
|
||||
switch (image->depth) {
|
||||
case 12: type = GL_UNSIGNED_SHORT_4_4_4_4; break; /* 12-bit (rare) */
|
||||
case 15: type = GL_UNSIGNED_SHORT_5_5_5_1; break; /* 15-bit, hi-color */
|
||||
case 16: /* 16-bit, hi-color */
|
||||
type = GL_UNSIGNED_SHORT_5_6_5;
|
||||
aligned = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
switch (image->depth) {
|
||||
case 24: type = GL_UNSIGNED_BYTE; break; /* 24-bit, true color */
|
||||
case 30: type = GL_UNSIGNED_INT_10_10_10_2; break; /* 30-bit, deep color */
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
if (image->depth == 48) /* 48-bit deep color */
|
||||
type = GL_UNSIGNED_SHORT;
|
||||
else goto invalid;
|
||||
break;
|
||||
/* >64-bit formats */
|
||||
case 128:
|
||||
if (image->depth == 96)
|
||||
type = GL_UNSIGNED_INT;
|
||||
else goto invalid;
|
||||
break;
|
||||
default:
|
||||
invalid: invalid = true;
|
||||
}
|
||||
|
||||
uint8_t* buf;
|
||||
if (invalid) {
|
||||
abort();
|
||||
/* Manual reformat (slow) */
|
||||
buf = malloc(4 * w * h);
|
||||
int xi, yi;
|
||||
Colormap map = DefaultColormap(d, DefaultScreen(d));
|
||||
for (yi = 0; yi < h; ++yi) {
|
||||
for (xi = 0; xi < w; ++xi) {
|
||||
c.pixel = XGetPixel(image, xi, yi);
|
||||
XQueryColor(d, map, &c);
|
||||
size_t base = (xi + (yi * w)) * 4;
|
||||
buf[base + 0] = c.red / 256;
|
||||
buf[base + 1] = c.green / 256;
|
||||
buf[base + 2] = c.blue / 256;
|
||||
buf[base + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
|
||||
free(buf);
|
||||
} else {
|
||||
/* Use image data directly. The alpha value is garbage/unassigned data, but
|
||||
we need to read it because X11 keeps pixel data aligned */
|
||||
buf = image->data;
|
||||
/* Data could be 2, 4, or 8 byte aligned, the RGBA format and type (depth)
|
||||
already ensures reads will be properly aligned across scanlines */
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
GLenum format = image->bitmap_bit_order == LSBFirst ?
|
||||
(!aligned ? GL_BGRA : GL_BGR) :
|
||||
(!aligned ? GL_RGBA : GL_RGB);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, format, GL_UNSIGNED_BYTE, buf);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* restore default */
|
||||
}
|
||||
|
||||
XFree(image);
|
||||
XCloseDisplay(d);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user