Page 1 of 1

Find color at current mouse location

Posted: Fri Apr 08, 2016 6:31 am
by wilbert
I'm looking for a cross platform way to find the color of the pixel at the current mouse location (even outside the application window).
Does anyone have an idea how to do this ?
Especially Linux seems complicated to me :?

Re: Find color at current mouse location

Posted: Fri Apr 08, 2016 6:47 am
by infratec
Windows solution

Code: Select all

; Samplecode to get the color of a Pixel on the Desktop
; by PureLust for PureBasic-Forum - 18.09.2009 (with thanks to netmaestro)  ;)
; extended by infratec, optimized by idle

OpenWindow(0, 0, 0, 200, 20, "", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)

TextGadget(0, 0, 3, 200, 20, "", #PB_Text_Center)


HDC = GetDC_(0)


Repeat
 Event = WaitWindowEvent(100)
  
 x = DesktopMouseX()
 y = DesktopMouseY()
 PixelColour = GetPixel_(hDC,x,y)
 Red = Red(PixelColour)
 Green = Green(PixelColour)
 Blue = Blue(PixelColour)
 PixelColour$ = "#" + RSet(Hex(Red), 2, "0") + RSet(Hex(Green), 2, "0") + RSet(Hex(Blue), 2, "0") 
 If WindowHeight(0) > 10
  SetWindowTitle(0, "GetPixelColour")
  PixelColour$ + " (" + Str(Red) + ", " + Str(Green) + ", " + Str(Blue) + ")"
  SetGadgetText(0, Str(x) + ","+ Str(y) +" : " + PixelColour$)
 Else
  SetWindowTitle(0, PixelColour$)
 EndIf
 
Until Event = #PB_Event_CloseWindow

ReleaseDC_(0,HDC)
Bernd

Re: Find color at current mouse location

Posted: Fri Apr 08, 2016 6:48 am
by wilbert
Thanks :)

Re: Find color at current mouse location

Posted: Fri Apr 08, 2016 7:57 am
by Keya
not sure about Linux either sorry but i got better results when adding GTK to my search!
perhaps this? http://rosettacode.org/wiki/Color_of_a_screen_pixel

Code: Select all

import gtk2, gdk2, gdk2pixbuf
gtk2.nim_init()
 
proc getPixelColor(x, y: int32): auto =
  var p = pixbufNew(COLORSPACE_RGB, false, 8, 1, 1)
  discard p.getFromDrawable(getDefaultRootWindow().Drawable,
    getDefaultScreen().getSystemColormap(), x, y, 0, 0, 1, 1)
  result = cast[tuple[r, g, b: uint8]](p.getPixels[])
 
echo getPixelColor(0, 0)

Code: Select all

def get_pixel_colour(i_x, i_y):
	import gtk # python-gtk2
	o_gdk_pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 1, 1)
	o_gdk_pixbuf.get_from_drawable(gtk.gdk.get_default_root_window(), gtk.gdk.colormap_get_system(), i_x, i_y, 0, 0, 1, 1)
	return tuple(o_gdk_pixbuf.get_pixels_array().tolist()[0][0])
 
print get_pixel_colour(0, 0)
returns answer in GdkPixbuf arrayish object http://gtk.php.net/manual/en/gdk.gdkpix ... awable.php:

Code: Select all

GdkPixbuf get_from_drawable(GdkWindow src, GdkColormap cmap, int src_x, int src_y, int dest_x, int dest_y, int width, int height);
Google also suggests you'd be the very first person to use this API in Purebasic! but i think there might be some GdkPixbuf-based Purebasic code at least

Re: Find color at current mouse location

Posted: Fri Apr 08, 2016 9:19 am
by Joris
Wilbert ? Er is toch :

Color = Point(x, y)
Return the color of a pixel in the current output.

Supported OS
All

Re: Find color at current mouse location

Posted: Fri Apr 08, 2016 9:46 am
by Keya
Joris: oh yeah! :oops: :)
i had a look at a linux binary created thats just "x=Point(0,0)" but i cant see what function its using in a hex editor!
hm but i guess wilbert also needs to know not just the pixel at x,y, but also where the cursor is

Re: Find color at current mouse location

Posted: Fri Apr 08, 2016 10:53 am
by wilbert
Point() only works within StartDrawing() / StopDrawing() so you can't access any pixel on the desktop.

Haven't tried Linux yet but am having a lot of problems on finding a way on OSX x64 as well.
Most methods I can find like using CGWindowListCreateImage() or NSReadPixel() require structures like CGRect or NSPoint to be passed by value which PB doesn't support. :?
Capturing the whole screen might be a possibility but seems a bit much if you only need one pixel.

Re: Find color at current mouse location

Posted: Fri Apr 08, 2016 11:01 am
by Joris
wilbert wrote:Point() only works within StartDrawing() / StopDrawing() so you can't access any pixel on the desktop....
I see, but then isn't StartDrawing not returning the DC ? Then you just need the handle.
I'm in doubt now... it's been a while.

Re: Find color at current mouse location

Posted: Fri Apr 08, 2016 12:51 pm
by wilbert
I found a way for OSX x64 but it was complicated to get it working; had to use asm to work around the problem of OSX not supporting passing of structures by value.

Code: Select all

; OSX 64 bit

ImportC ""
  CGBitmapContextCreate(*bdata, width, height, bitsPerComponent, bytesPerRow, cs, bitmapInfo)
  CGColorSpaceCreateDeviceRGB()
  CGColorSpaceRelease(cs)
  CGContextRelease(c)
  CGImageRelease(image)  
EndImport

Procedure CGContextDrawImage_addr()
  ProcedureReturn ?cgcdi_start
  cgcdi_start:
  !extern _CGContextDrawImage
  !sub rsp, 40
  !movq [rsp     ], xmm0
  !movq [rsp +  8], xmm1
  !movq [rsp + 16], xmm2
  !movq [rsp + 24], xmm3
  !call _CGContextDrawImage
  !add rsp, 40
  !ret
EndProcedure

Procedure CGWindowListCreateImage_addr()
  ProcedureReturn ?cgwlci_start
  cgwlci_start:
  !extern _CGWindowListCreateImage
  !sub rsp, 40
  !movq [rsp     ], xmm0
  !movq [rsp +  8], xmm1
  !movq [rsp + 16], xmm2
  !movq [rsp + 24], xmm3
  !call _CGWindowListCreateImage
  !add rsp, 40
  !ret
EndProcedure

Prototype CGContextDrawImage_proto(c, x.d, y.d, w.d, h.d, image)
Prototype CGWindowListCreateImage_proto(x.d, y.d, w.d, h.d, listOption, windowID, imageOption)
Global CGWindowListCreateImage.CGWindowListCreateImage_proto = CGWindowListCreateImage_addr()
Global CGContextDrawImage.CGContextDrawImage_proto = CGContextDrawImage_addr()

Procedure.l GetColorUnderMouse()
  Protected.i image, csRGB, ctx, c.l
  image = CGWindowListCreateImage(DesktopMouseX(), DesktopMouseY(), 1, 1, 1, 0, 0)
  csRGB = CGColorSpaceCreateDeviceRGB()
  ctx = CGBitmapContextCreate(@c, 1, 1, 8, 4, csRGB, 1)
  CGContextDrawImage(ctx, 0, 0, 1, 1, image)
  CGColorSpaceRelease(csRGB)
  CGImageRelease(image)
  CGContextRelease(ctx)
  ProcedureReturn c
EndProcedure
or a bit shorter

Code: Select all

; OSX 64 bit

ImportC ""
  CGBitmapContextCreate(*bdata, width, height, bitsPerComponent, bytesPerRow, cs, bitmapInfo)
  CGColorSpaceCreateDeviceRGB()
  CGColorSpaceRelease(cs)
  CGContextRelease(c)
  CGImageRelease(image)  
EndImport

Procedure.l GetColorUnderMouse()
  Protected.i image, csRGB, ctx
  Protected c.l, x.d, y.d, one.d = 1
  x = DesktopMouseX()
  y = DesktopMouseY()
  csRGB = CGColorSpaceCreateDeviceRGB()
  ctx = CGBitmapContextCreate(@c, 1, 1, 8, 4, csRGB, 1)
  CGColorSpaceRelease(csRGB)
  !extern _CGContextDrawImage
  !extern _CGWindowListCreateImage
  !movsd xmm0, [p.v_x]
  !movsd xmm1, [p.v_y]
  !movsd xmm2, [p.v_one]
  !sub rsp, 32
  !movsd [rsp     ], xmm0
  !movsd [rsp +  8], xmm1
  !movsd [rsp + 16], xmm2
  !movsd [rsp + 24], xmm2
  !mov rdi, 1
  !xor rsi, rsi
  !xor rdx, rdx
  !call _CGWindowListCreateImage
  !add rsp, 32
  !mov [p.v_image], rax
  !mov rdi, [p.v_ctx]
  !mov rsi, rax
  !pxor xmm0, xmm0
  !movsd xmm2, [p.v_one]
  !sub rsp, 32
  !movupd [rsp], xmm0
  !movsd [rsp + 16], xmm2
  !movsd [rsp + 24], xmm2  
  !call _CGContextDrawImage
  !add rsp, 32
  CGImageRelease(image)
  CGContextRelease(ctx)
  ProcedureReturn c
EndProcedure

Re: Find color at current mouse location

Posted: Fri Apr 08, 2016 3:43 pm
by wilbert
This seems to work on Linux gtk3 except that it only works for the main screen.

Code: Select all

ImportC ""
  gdk_pixbuf_get_from_window(window, x, y, width, height)
  gdk_pixbuf_get_pixels(pixbuf)
EndImport

Procedure.l GetColorUnderMouse()
  Protected.i pixbuf, *buf.Ascii, c.l
  pixbuf = gdk_pixbuf_get_from_window(gdk_get_default_root_window_(), DesktopMouseX(), DesktopMouseY(), 1, 1)
  *buf = gdk_pixbuf_get_pixels(pixbuf)
  c = *buf\a : *buf + 1
  c | *buf\a << 8 : *buf + 1
  c | *buf\a << 16
  g_object_unref_(pixbuf)
  ProcedureReturn c
EndProcedure
If someone knows a solution that works across multiple monitors, please let me know.

Re: Find color at current mouse location

Posted: Tue Sep 13, 2016 7:25 pm
by Oma
Hi Wilbert!
First of all thank you for the routines.
... it only works for the main screen. If someone knows a solution that works across multiple monitors, please let me know.
I now have tested your Linux version on many distributions.
Your picker routine works everywhere on both monitors and if you make the parent window (from picker) sticky, even on all desktops. However, the picker window must of course have the focus. I trigger the picker from the space-bar.

You are talking about monitors and screens in Linux. There are still the terms display and desktop in the API so it's slightly confusing what you mean exactly?

Best regards, Charly

Re: Find color at current mouse location

Posted: Tue Sep 13, 2016 9:24 pm
by wilbert
Hi Charly,
Oma wrote:Your picker routine works everywhere on both monitors and if you make the parent window (from picker) sticky, even on all desktops. However, the picker window must of course have the focus. I trigger the picker from the space-bar.
Thanks for your feedback. I forgot to post about the cause.
Shardik also confirmed it worked. My conclusion is that it has to do with the fact that I'm running Linux inside a Parallels VM.
Unfortunately this can cause unwanted limitations sometimes. :(