JXA - Get Pixel color from screen coord

Probably more useful for scripters… Ported from here:

function pixelColorAtScreenCoord(displayID,x,y){
   ObjC.import('Foundation')
   ObjC.import('AppKit')
   image = $.CGDisplayCreateImageForRect(displayID,$.CGRectMake(x,y,1,1));
   bitmap = $.NSBitmapImageRep.alloc.initWithCGImage(image)
   $.CGImageRelease(image)
   color = bitmap.colorAtXY($(x),$(y))
bitmap.release
return [
           parseInt(255*color.redComponent),    
           parseInt(255*color.greenComponent),
           parseInt(255*color.blueComponent),
           color.alphaComponent
        ];
}
1 Like

The routine is more rounded now without need to grab the displayID before running the function:

function pixelColorAtScreenCoord(x,y){
   ObjC.import('Foundation')
   ObjC.import('AppKit')
   rect = $.CGRectMake(x,y,1,1)

   //Get DisplayID from point location.
   var displays = Ref()
   $.CGMainDisplayID() //Fully initialise CoreGraphics Framework
   maxDisplays = 5
   $.CGGetDisplaysWithRect(rect,maxDisplays,displays,null)
   displayID = displays[0]
   displays.release

   image = $.CGDisplayCreateImageForRect(displayID,rect);
   bitmap = $.NSBitmapImageRep.alloc.initWithCGImage(image)
   $.CGImageRelease(image)
   color = bitmap.colorAtXY($(x),$(y))
   bitmap.release
   return [
           parseInt(255*color.redComponent),
           parseInt(255*color.greenComponent),
           parseInt(255*color.blueComponent),
           color.alphaComponent
        ];
}

Of course, do also remember that everything added to the function will decrease it’s speed. If you know which screen to get the pixels from it may still be better using the first function on this page.

Note: The maxDisplays variable is the size of the displays array. This value determines the maximum number of displayIDs that can be returned. I.E. if someone has 6 displays and maxDisplays=5 then there is a 1/6 chance that a randomly selected pixel’s color cannot be extracted. For this reason it may be wise to set maxDisplays to a large, unrealistic value such as 99, however I still do not know the drawbacks of doing this.

This is wonderful — thank you for finding and porting it. Could you please turn this into a macro that would get the current x, y of the mouse and screen and feed those into the function? Use a “Set Variable to Text” action, and set its body to %Calculate%MOUSEX()%, %Calculate%MOUSEY()%. You would to figure out which screen the mouse is on; I don’t know how to do that. Then it would be of use to non-scripters too!

2 Likes

Personally, I try to stick away from making unnecessary switches between software packages unless necessary (e.g. KM's image search is a good example of something which should probably not be implemented in JXA...)

function displayIDFromMousePos(){
   ObjC.import('Foundation')
   ObjC.import('AppKit')
   pnt = $.NSEvent.mouseLocation

   //Get DisplayID from point location.
   var displays = Ref()
   $.CGMainDisplayID() //Fully initialise CoreGraphics Framework
   maxDisplays = 32
   $. CGGetDisplaysWithPoint(pnt,maxDisplays,displays,null)
   displayID = displays[0]
  
   return displayID
}

Bear in mind that the displayID might not be as useful as you think it is... For instance on mine:

Screen | ID
1      | 1127233615
2      | 480985424

There doesn't appear to be a pattern as such. Note: The top left hand corner of the Main Display has displayID=0 - I'm not totally sure why.

Of course you can check whether the mouse is on the Main display or not using:

function isMouseOnMain(){
   ObjC.import('Foundation')
   ObjC.import('AppKit')

   //Get DisplayID from point location.
   return $.CGMainDisplayID() == displayIDFromMousePos()
}

If you want to return these variables to keyboard maestro you might want to ask @DanThomas. I don't use keyboard maestro much these days as I work with mainly Windows PCs. One day I will return but not for a while!

Hope this helps!