Last active
July 15, 2022 03:36
-
-
Save Youka/193afdec83321f4f51a2 to your computer and use it in GitHub Desktop.
Picking colors at mouse position with LuaJIT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Load FFI | |
local ffi = require("ffi") | |
-- Define FFI functions & structures by OS | |
local x11 | |
if ffi.os == "Windows" then | |
ffi.cdef([[ | |
typedef int BOOL; | |
typedef long LONG; | |
typedef struct{ | |
LONG x, y; | |
}POINT, *LPPOINT; | |
typedef void* HANDLE; | |
typedef HANDLE HDC; | |
typedef HANDLE HWND; | |
typedef unsigned long DWORD; | |
typedef DWORD COLORREF; | |
BOOL GetCursorPos(LPPOINT); | |
HDC GetDC(HWND); | |
int ReleaseDC(HWND, HDC); | |
COLORREF GetPixel(HDC, int, int); | |
]]) | |
else | |
x11 = ffi.load("X11") | |
ffi.cdef( | |
[[ | |
typedef void Display; | |
typedef unsigned long XID; | |
typedef XID Window; | |
typedef XID Colormap; | |
typedef struct{ | |
void* ext_data; | |
void* display; | |
Window root; | |
int width, height; | |
int mwidth, mheight; | |
int ndepths; | |
void* depths; | |
int root_depth; | |
void* root_visual; | |
void* default_gc; | |
Colormap cmap; | |
// Rest doesn't matter | |
}Screen; | |
typedef char* XPointer; | |
typedef struct{ | |
void* ext_data; | |
void* private1; | |
int fd; | |
int private2; | |
int proto_major_version; | |
int proto_minor_version; | |
char* vendor; | |
XID private3; | |
XID private4; | |
XID private5; | |
int private6; | |
XID (*resource_alloc)(void*); | |
int byte_order; | |
int bitmap_unit; | |
int bitmap_pad; | |
int bitmap_bit_order; | |
int nformats; | |
void* pixmap_format; | |
int private8; | |
int release; | |
void* private9, *private10; | |
int qlen; | |
unsigned long last_request_read; | |
unsigned long request; | |
XPointer private11; | |
XPointer private12; | |
XPointer private13; | |
XPointer private14; | |
unsigned max_request_size; | |
void* db; | |
int (*private15)(void*); | |
char* display_name; | |
int default_screen; | |
// Rest doesn't matter | |
}*_XPrivDisplay; | |
typedef int Bool; | |
typedef struct{ | |
int x, y; | |
int width, height; | |
int border_width; | |
int depth; | |
void* visual; | |
Window root; | |
int class; | |
int bit_gravity; | |
int win_gravity; | |
int backing_store; | |
unsigned long backing_planes; | |
unsigned long backing_pixel; | |
Bool save_under; | |
Colormap colormap; | |
Bool map_installed; | |
int map_state; | |
long all_event_masks; | |
long your_event_mask; | |
long do_not_propagate_mask; | |
Bool override_redirect; | |
Screen *screen; | |
}XWindowAttributes; | |
typedef void XImage; | |
typedef XID Drawable; | |
static const unsigned long AllPlanes = (unsigned long)~0L; | |
static const int XYPixmap = 1; | |
typedef struct{ | |
unsigned long pixel; | |
unsigned short red, green, blue; | |
char flags; | |
char pad; | |
}XColor; | |
Display* XOpenDisplay(char*); | |
int XCloseDisplay(Display*); | |
Screen* XScreenOfDisplay(Display*, int); | |
Bool XQueryPointer(Display*, Window, Window*, Window*, int*, int*, int*, int*, unsigned int*); | |
int XGetWindowAttributes(Display*, Window, XWindowAttributes*); | |
XImage* XGetImage(Display*, Drawable, int, int, unsigned int, unsigned int, unsigned long, int); | |
int XFree(void*); | |
unsigned long XGetPixel(XImage*, int, int); | |
Colormap XDefaultColormap(Display*, int); | |
int XQueryColor(Display*, Colormap, XColor*); | |
]] | |
) | |
end | |
-- Get screen color(s) at cursor position and cursor position | |
local function pick_screen_color(range) | |
-- Check arguments | |
if range ~= nil and (type(range) ~= "number" or range < 0) then | |
error("Invalid color pick range!", 2) | |
end | |
-- Fix range to valid integer | |
range = range and math.floor(range) or 0 | |
-- Output buffers | |
local bmp, mx, my = {n = 0} | |
-- Continue by OS | |
if ffi.os == "Windows" then | |
-- Get cursor position | |
local ppoint = ffi.new("POINT[1]") | |
if ffi.C.GetCursorPos(ppoint) == 0 then | |
error("Couldn't get cursor position!", 2) | |
end | |
mx, my = ppoint[0].x, ppoint[0].y | |
-- Get desktop device context | |
local dc = ffi.gc(ffi.C.GetDC(nil), function(dc) ffi.C.ReleaseDC(nil, dc) end) | |
if not dc then | |
error("Couldn't get desktop device context!", 2) | |
end | |
-- Get desktop color under & around cursor | |
for y=-range, range do | |
for x=-range, range do | |
bmp.n = bmp.n + 1 | |
local color = ffi.C.GetPixel(dc, mx + x, my + y) | |
if color < 16777216 then -- No error on pixel get | |
bmp[bmp.n] = {r = color % 256, g = math.floor(color / 256) % 256, b = math.floor(color / 65536)} | |
end | |
end | |
end | |
else | |
-- Get display & root window | |
local display = ffi.gc(x11.XOpenDisplay(nil), x11.XCloseDisplay) | |
if not display then | |
error("Couldn't open display!", 2) | |
end | |
local root = x11.XScreenOfDisplay(display, ffi.cast("_XPrivDisplay", display)[0].default_screen)[0].root | |
-- Get cursor position | |
local root_window, child_window, root_x, root_y, win_x, win_y, mask = ffi.new("Window[1]"), ffi.new("Window[1]"), ffi.new("int[1]"), ffi.new("int[1]"), ffi.new("int[1]"), ffi.new("int[1]"), ffi.new("unsigned int[1]") | |
if x11.XQueryPointer(display, root, root_window, child_window, root_x, root_y, win_x, win_y, mask) == 0 then | |
error("Couldn't get cursor position!", 2) | |
end | |
mx, my = win_x[0], win_y[0] | |
-- Get desktop color under & around cursor | |
local attr = ffi.new("XWindowAttributes[1]") | |
if x11.XGetWindowAttributes(display, root, attr) == 0 then | |
error("Couldn't get window attributes!", 2) | |
end | |
local w, h, color, color_map = attr[0].width, attr[0].height, ffi.new("XColor[1]"), x11.XScreenOfDisplay(display, ffi.cast("_XPrivDisplay", display)[0].default_screen)[0].cmap | |
for y=-range, range do | |
for x=-range, range do | |
bmp.n = bmp.n + 1 | |
local ix, iy = mx + x, my + y | |
if ix >= 0 and iy >= 0 and ix < w and iy < h then | |
local image = ffi.gc(x11.XGetImage(display, root, ix, iy, 1, 1, ffi.C.AllPlanes, ffi.C.XYPixmap), x11.XFree) | |
if image then | |
color[0].pixel = x11.XGetPixel(image, 0, 0) | |
x11.XQueryColor(display, color_map, color) | |
bmp[bmp.n] = {r = math.floor(color[0].red / 256), g = math.floor(color[0].green / 256), b = math.floor(color[0].blue / 256)} | |
end | |
end | |
end | |
end | |
end | |
-- Return collected pixels & mouse position | |
return bmp, mx, my | |
end | |
-- Test | |
local colors, mouse_x, mouse_y = pick_screen_color(1) | |
print(mouse_x, mouse_y) | |
for i=1, colors.n do | |
local color = colors[i] | |
if color then | |
print(color.r, color.g, color.b) | |
else | |
print(color) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment