UI: Allow Eyedropper Outside of Blender

This adds a new Ghost function, GHOST_GetPixelAtCursor, that allows
picking colors from outside of Blender windows. This only has an
implementation for the Windows platform, but this should allow other
platforms to also do so if possible.

Pull Request: https://projects.blender.org/blender/blender/pulls/105324
This commit is contained in:
Harley Acheson
2023-08-16 01:14:36 +02:00
committed by Harley Acheson
parent 077db81638
commit 5741a5d433
17 changed files with 121 additions and 26 deletions

View File

@@ -764,6 +764,13 @@ extern void GHOST_SetMultitouchGestures(GHOST_SystemHandle systemhandle, const b
*/
extern void GHOST_SetTabletAPI(GHOST_SystemHandle systemhandle, GHOST_TTabletAPI api);
/**
* Get the color of the pixel at the current mouse cursor location
* \param r_color: returned sRGB float colors
* \return Success value (true == successful and supported by platform)
*/
extern GHOST_TSuccess GHOST_GetPixelAtCursor(float r_color[3]);
/**
* Access to rectangle width.
* \param rectanglehandle: The handle to the rectangle.

View File

@@ -441,6 +441,13 @@ class GHOST_ISystem {
*/
virtual void setTabletAPI(GHOST_TTabletAPI api) = 0;
/**
* Get the color of the pixel at the current mouse cursor location
* \param r_color: returned sRGB float colors
* \return Success value (true == successful and supported by platform)
*/
virtual GHOST_TSuccess getPixelAtCursor(float r_color[3]) const = 0;
#ifdef WITH_INPUT_NDOF
/**
* Sets 3D mouse deadzone

View File

@@ -107,6 +107,10 @@ typedef enum {
* Set when there is support for system clipboard copy/paste.
*/
GHOST_kCapabilityClipboardImages = (1 << 4),
/**
* Support for sampling a color outside of the Blender windows.
*/
GHOST_kCapabilityDesktopSample = (1 << 5),
} GHOST_TCapabilityFlag;
/**
@@ -115,7 +119,8 @@ typedef enum {
*/
#define GHOST_CAPABILITY_FLAG_ALL \
(GHOST_kCapabilityCursorWarp | GHOST_kCapabilityWindowPosition | \
GHOST_kCapabilityPrimaryClipboard | GHOST_kCapabilityGPUReadFrontBuffer)
GHOST_kCapabilityPrimaryClipboard | GHOST_kCapabilityGPUReadFrontBuffer | \
GHOST_kCapabilityClipboardImages | GHOST_kCapabilityDesktopSample)
/* Xtilt and Ytilt represent how much the pen is tilted away from
* vertically upright in either the X or Y direction, with X and Y the

View File

@@ -768,6 +768,12 @@ void GHOST_SetTabletAPI(GHOST_SystemHandle systemhandle, GHOST_TTabletAPI api)
system->setTabletAPI(api);
}
GHOST_TSuccess GHOST_GetPixelAtCursor(float r_color[3])
{
GHOST_ISystem *system = GHOST_ISystem::getSystem();
return system->getPixelAtCursor(r_color);
}
int32_t GHOST_GetWidthRectangle(GHOST_RectangleHandle rectanglehandle)
{
return ((GHOST_Rect *)rectanglehandle)->getWidth();

View File

@@ -341,6 +341,11 @@ GHOST_TTabletAPI GHOST_System::getTabletAPI()
return m_tabletAPI;
}
GHOST_TSuccess GHOST_System::getPixelAtCursor(float[3] /* r_color */) const
{
return GHOST_kFailure;
}
#ifdef WITH_INPUT_NDOF
void GHOST_System::setNDOFDeadZone(float deadzone)
{

View File

@@ -256,6 +256,13 @@ class GHOST_System : public GHOST_ISystem {
virtual void setTabletAPI(GHOST_TTabletAPI api);
GHOST_TTabletAPI getTabletAPI(void);
/**
* Get the color of the pixel at the current mouse cursor location
* \param r_color: returned sRGB float colors
* \return Success value (true == successful and supported by platform)
*/
GHOST_TSuccess getPixelAtCursor(float r_color[3]) const;
#ifdef WITH_INPUT_NDOF
/***************************************************************************************
* Access to 3D mouse.

View File

@@ -921,6 +921,8 @@ GHOST_TCapabilityFlag GHOST_SystemCocoa::getCapabilities() const
~(
/* Cocoa has no support for a primary selection clipboard. */
GHOST_kCapabilityPrimaryClipboard |
/* Cocoa has no support for sampling colors from the desktop. */
GHOST_kCapabilityDesktopSample |
/* This Cocoa back-end has not yet implemented image copy/paste. */
GHOST_kCapabilityClipboardImages));
}

View File

@@ -50,6 +50,7 @@ class GHOST_SystemHeadless : public GHOST_System {
/* No windowing functionality supported. */
~(GHOST_kCapabilityWindowPosition | GHOST_kCapabilityCursorWarp |
GHOST_kCapabilityPrimaryClipboard |
GHOST_kCapabilityDesktopSample |
GHOST_kCapabilityClipboardImages));
}
char *getClipboard(bool /*selection*/) const override

View File

@@ -772,6 +772,8 @@ GHOST_TCapabilityFlag GHOST_SystemSDL::getCapabilities() const
~(
/* This SDL back-end has not yet implemented primary clipboard. */
GHOST_kCapabilityPrimaryClipboard |
/* This SDL back-end has not yet implemented color sampling the desktop. */
GHOST_kCapabilityDesktopSample |
/* This SDL back-end has not yet implemented image copy/paste. */
GHOST_kCapabilityClipboardImages));
}

View File

@@ -6843,6 +6843,8 @@ GHOST_TCapabilityFlag GHOST_SystemWayland::getCapabilities() const
* screen-shot and eye-dropper sampling logic, both operations where the overhead
* is negligible. */
GHOST_kCapabilityGPUReadFrontBuffer |
/* This WAYLAND back-end has not yet implemented desktop color sample. */
GHOST_kCapabilityDesktopSample |
/* This WAYLAND back-end has not yet implemented image copy/paste. */
GHOST_kCapabilityClipboardImages));
}

View File

@@ -448,6 +448,31 @@ GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(int32_t x, int32_t y)
return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
}
GHOST_TSuccess GHOST_SystemWin32::getPixelAtCursor(float r_color[3]) const
{
POINT point;
if (!GetCursorPos(&point)) {
return GHOST_kFailure;
}
HDC dc = GetDC(NULL);
if (dc == NULL) {
return GHOST_kFailure;
}
COLORREF color = GetPixel(dc, point.x, point.y);
ReleaseDC(NULL, dc);
if (color == CLR_INVALID) {
return GHOST_kFailure;
}
r_color[0] = GetRValue(color) / 255.0f;
r_color[1] = GetGValue(color) / 255.0f;
r_color[2] = GetBValue(color) / 255.0f;
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) const
{
/* `GetAsyncKeyState` returns the current interrupt-level state of the hardware, which is needed

View File

@@ -182,6 +182,13 @@ class GHOST_SystemWin32 : public GHOST_System {
*/
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y);
/**
* Get the color of the pixel at the current mouse cursor location
* \param r_color: returned sRGB float colors
* \return Success value (true == successful and supported by platform)
*/
GHOST_TSuccess getPixelAtCursor(float r_color[3]) const;
/***************************************************************************************
** Access to mouse button and keyboard states.
***************************************************************************************/

View File

@@ -1693,6 +1693,8 @@ GHOST_TCapabilityFlag GHOST_SystemX11::getCapabilities() const
{
return GHOST_TCapabilityFlag(GHOST_CAPABILITY_FLAG_ALL &
~(
/* No support yet for desktop sampling. */
GHOST_kCapabilityDesktopSample |
/* No support yet for image copy/paste. */
GHOST_kCapabilityClipboardImages));
}

View File

@@ -315,45 +315,35 @@ static bool eyedropper_cryptomatte_sample_fl(bContext *C,
void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3])
{
/* we could use some clever */
Main *bmain = CTX_data_main(C);
const char *display_device = CTX_data_scene(C)->display_settings.display_device;
ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
wmWindowManager *wm = CTX_wm_manager(C);
ScrArea *area = nullptr;
int mval[2];
wmWindow *win;
ScrArea *area;
datadropper_win_area_find(C, m_xy, mval, &win, &area);
wmWindow *win = WM_window_find_under_cursor(CTX_wm_window(C), m_xy, mval);
if (win) {
bScreen *screen = WM_window_get_active_screen(win);
area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mval);
}
if (area) {
if (area->spacetype == SPACE_IMAGE) {
ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
if (region) {
ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
if (region) {
const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
if (area->spacetype == SPACE_IMAGE) {
SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first);
const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
if (ED_space_image_color_sample(sima, region, region_mval, r_col, nullptr)) {
return;
}
}
}
else if (area->spacetype == SPACE_NODE) {
ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
if (region) {
else if (area->spacetype == SPACE_NODE) {
SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
Main *bmain = CTX_data_main(C);
if (ED_space_node_color_sample(bmain, snode, region, region_mval, r_col)) {
return;
}
}
}
else if (area->spacetype == SPACE_CLIP) {
ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_WINDOW, mval);
if (region) {
else if (area->spacetype == SPACE_CLIP) {
SpaceClip *sc = static_cast<SpaceClip *>(area->spacedata.first);
const int region_mval[2] = {mval[0] - region->winrct.xmin, mval[1] - region->winrct.ymin};
if (ED_space_clip_color_sample(sc, region, region_mval, r_col)) {
return;
}
@@ -362,9 +352,20 @@ void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3])
}
if (win) {
WM_window_pixels_read_sample(C, win, mval, r_col);
/* Other areas within a Blender window. */
if (!WM_window_pixels_read_sample(C, win, mval, r_col)) {
WM_window_pixels_read_sample_from_offscreen(C, win, mval, r_col);
}
const char *display_device = CTX_data_scene(C)->display_settings.display_device;
ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
IMB_colormanagement_display_to_scene_linear_v3(r_col, display);
}
else if ((WM_capabilities_flag() & WM_CAPABILITY_DESKTOP_SAMPLE) &&
WM_desktop_cursor_sample_read(r_col))
{
/* Outside of the Blender window if we support it. */
IMB_colormanagement_srgb_to_scene_linear_v3(r_col, r_col);
}
else {
zero_v3(r_col);
}

View File

@@ -170,6 +170,8 @@ enum eWM_CapabilitiesFlag {
WM_CAPABILITY_GPU_FRONT_BUFFER_READ = (1 << 3),
/** Ability to copy/paste system clipboard images. */
WM_CAPABILITY_CLIPBOARD_IMAGES = (1 << 4),
/** Ability to sample a color outside of Blender windows. */
WM_CAPABILITY_DESKTOP_SAMPLE = (1 << 5),
/** The initial value, indicates the value needs to be set by inspecting GHOST. */
WM_CAPABILITY_INITIALIZED = (1 << 31),
};
@@ -194,6 +196,12 @@ wmWindow *WM_window_find_under_cursor(wmWindow *win, const int mval[2], int r_mv
*/
wmWindow *WM_window_find_by_area(wmWindowManager *wm, const ScrArea *area);
/**
* Return the color of the pixel at the current mouse cursor position on the desktop, whether in a
* Blender window or not. Returns false on failure or if not supported by the platform.
*/
bool WM_desktop_cursor_sample_read(float r_col[3]);
/**
* Read pixels from the front-buffer (fast).
*

View File

@@ -1411,6 +1411,11 @@ bool WM_window_pixels_read_sample(bContext *C, wmWindow *win, const int pos[2],
return WM_window_pixels_read_sample_from_offscreen(C, win, pos, r_col);
}
bool WM_desktop_cursor_sample_read(float r_col[3])
{
return GHOST_GetPixelAtCursor(r_col);
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -1887,6 +1887,9 @@ eWM_CapabilitiesFlag WM_capabilities_flag()
if (ghost_flag & GHOST_kCapabilityClipboardImages) {
flag |= WM_CAPABILITY_CLIPBOARD_IMAGES;
}
if (ghost_flag & GHOST_kCapabilityDesktopSample) {
flag |= WM_CAPABILITY_DESKTOP_SAMPLE;
}
return flag;
}