首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Linux上从C++屏幕上获取像素颜色

在Linux上从C++屏幕上获取像素颜色
EN

Code Review用户
提问于 2020-03-27 06:03:01
回答 1查看 1.7K关注 0票数 2

在很长一段时间里,我一直在研究这段代码,主要是为了理解它。这是需要审查的主要原因。第二个原因很简单,我想以你能找到的任何方式来优化它。

附带注意:我已经尽可能地缩短了代码。

汇编以下代码:

代码语言:javascript
复制
g++-8 -O2 -std=c++17 -Wall -Wextra -Werror -Wpedantic -pedantic-errors getPixelColor.cpp -o getPixelColor $(pkg-config --cflags --libs x11)
代码语言:javascript
复制
#include <iostream>
#include <X11/Xlib.h>
#include <cmath> // round()

// https://stackoverflow.com/a/59663458/1997354
inline __attribute__((always_inline)) \
unsigned long MyXGetPixel(XImage * ximage, const int & x, const int & y)
{
    return (*ximage->f.get_pixel)(ximage, x, y);
}

void MyGetPixelColor(Display * my_display, const int & x_coord, const int & y_coord, XColor * pixel_color)
{
    XImage * screen_image = XGetImage(
        my_display,
        XRootWindow(my_display, XDefaultScreen(my_display)),
        x_coord, y_coord,
        1, 1,
        AllPlanes,
        XYPixmap
    );
    pixel_color->pixel = MyXGetPixel(screen_image, 0, 0);
    XFree(screen_image);
    XQueryColor(my_display, XDefaultColormap(my_display, XDefaultScreen(my_display)), pixel_color);
}

// usage
int main()
{

Display * my_display = XOpenDisplay(NULL);
XColor screen_pixel_color;
// in this example let's take [0,0] coordinates
MyGetPixelColor(my_display, 0, 0, & screen_pixel_color);
XCloseDisplay(my_display);

unsigned short raw_r_value = screen_pixel_color.red;
unsigned short raw_g_value = screen_pixel_color.green;
unsigned short raw_b_value = screen_pixel_color.blue;

std::cout
    << "R = " << round(raw_r_value / 256.0) << std::endl
    << "G = " << round(raw_g_value / 256.0) << std::endl
    << "B = " << round(raw_b_value / 256.0) << std::endl;

}

未缩短的全码

代码语言:javascript
复制
#include <iostream>
#include <X11/Xlib.h>
#include <cmath> // round()
//#include <cstring>
//#include <cstdlib>
//#include <gdk/gdk.h>
//#include <gtk/gtk.h>
//#include <X11/Xutil.h>
//#include </usr/include/gtk-3.0/gtk/gtk.h>
//#include <ncurses.h>

class BasicCoordinates
{
private:
    long long X;
    long long Y;

public:
    BasicCoordinates(const long long & _X, const long long & _Y)
    {
        X = _X;
        Y = _Y;
    }
    long long getX()
    {
        return X;
    }
    long long getY()
    {
        return Y;
    }

};

bool StringContainsInteger(const std::string & str)
// true : if the string contains an integer number (possibly starting with a sign)
// false: if the string contains some other character(s)
{
    std::string::size_type str_len = str.length();
    if (str_len == 0) return false;

    bool sign_present = (str[0] == '-' || str[0] == '+');
    if (str_len == 1 && sign_present) return false;

    for (std::string::size_type i = 0; i < str_len; i++)
    {
        if (i == 0 && sign_present) continue;
        if (! std::isdigit((unsigned char) str[i])) return false;
    }

    return true;
}


// https://stackoverflow.com/a/59663458/1997354
inline __attribute__((always_inline)) \
unsigned long MyXGetPixel(XImage * ximage, const int & x, const int & y)
{
    return (*ximage->f.get_pixel)(ximage, x, y);
}

void MyGetPixelColor(Display * my_display, const int & x_coord, const int & y_coord, XColor * pixel_color)
{
    XImage * screen_image = XGetImage(
        my_display,
        XRootWindow(my_display, XDefaultScreen(my_display)),
        x_coord, y_coord,
        1, 1,
        AllPlanes,
        XYPixmap
    );
    pixel_color->pixel = MyXGetPixel(screen_image, 0, 0);
    XFree(screen_image);
    XQueryColor(my_display, XDefaultColormap(my_display, XDefaultScreen(my_display)), pixel_color);
}




int main(const int argc, const char * argv[])
{

    const int given_arguments_count = argc - 1;
    if (given_arguments_count != 2)
    {
        std::cerr
            << "Fatal error occurred while checking\n"
            << "the number of given arguments\n"
            << "--------------------------------------\n"
            << "In the function : " << __func__ << std::endl
            << "At the command  : " << "given_arguments_count\n"
            << "Given arguments : " << given_arguments_count << std::endl
            << "Error message   : " << "This program is expecting exactly 2 arguments\n"
            << "                  being the X Y coordinates of a pixel on the screen\n";
        return 1;
    }

    const std::string cli_argument_1 = argv[1];
    if (! StringContainsInteger(cli_argument_1))
    {
        std::cerr
            << "Fatal error occurred while checking\n"
            << "if the first argument contains a number\n"
            << "----------------------------------------\n"
            << "In the function : " << __func__ << std::endl
            << "At the command  : " << "StringContainsInteger\n"
            << "Input string    : " << cli_argument_1 << std::endl
            << "Error message   : " << "The first argument is not an integer number\n";
        return 1;
    }

    const std::string cli_argument_2 = argv[2];
    if (! StringContainsInteger(cli_argument_2))
    {
        std::cerr
            << "Fatal error occurred while checking\n"
            << "if the second argument contains a number\n"
            << "----------------------------------------\n"
            << "In the function : " << __func__ << std::endl
            << "At the command  : " << "StringContainsInteger\n"
            << "Input string    : " << cli_argument_2 << std::endl
            << "Error message   : " << "The second argument is not an integer number\n";
        return 1;
    }

    long long x_coord;
    try
    {
        x_coord = std::stoll(cli_argument_1);
    }
    catch (const std::exception & input_exception)
    {
        std::cerr
            << "Fatal error occurred while converting\n"
            << "the first argument to an integer variable\n"
            << "-------------------------------------\n"
            << "In the function : " << __func__ << std::endl
            << "At the command  : " << input_exception.what() << std::endl
            << "Input string    : " << cli_argument_1 << std::endl
            << "Error message   : " << "The first number argument is too big an integer\n";
        return 1;
    }

    long long y_coord;
    try
    {
        y_coord = std::stoll(cli_argument_2);
    }
    catch (const std::exception & input_exception)
    {
        std::cerr
            << "Fatal error occurred while converting\n"
            << "the second argument to an integer variable\n"
            << "--------------------------------------\n"
            << "In the function : " << __func__ << std::endl
            << "At the command  : " << input_exception.what() << std::endl
            << "Input string    : " << cli_argument_2 << std::endl
            << "Error message   : " << "The second number argument is too big an integer\n";
        return 1;
    }


    BasicCoordinates * pixel_coords = new BasicCoordinates(x_coord, y_coord);
    std::cout << "X = " << pixel_coords->getX() << std::endl;
    std::cout << "Y = " << pixel_coords->getY() << std::endl << std::endl;









        Display * my_display = XOpenDisplay(NULL);
        Screen * my_screen = XDefaultScreenOfDisplay(my_display);

        const int screen_width = my_screen->width;
        const int screen_height = my_screen->height;

        if (x_coord >= screen_width)
        {
            std::cerr
                << "X coord bigger than the screen with\n"; // TEMP

            return 1;
        }

        if (y_coord >= screen_height)
        {
            std::cerr
                << "Y coord bigger than the screen height\n"; // TEMP

            return 1;
        }



        XWindowAttributes root_window_attributes;
        XGetWindowAttributes(my_display, DefaultRootWindow(my_display), & root_window_attributes);


        XColor screen_pixel_color;


        MyGetPixelColor(my_display, x_coord, y_coord, & screen_pixel_color);

        XCloseDisplay(my_display);



        unsigned short raw_r_value = screen_pixel_color.red;
        unsigned short raw_g_value = screen_pixel_color.green;
        unsigned short raw_b_value = screen_pixel_color.blue;
/*
        std::cout
            << "Raw Values" << std::endl
            << "R = " << raw_r_value << std::endl
            << "G = " << raw_g_value << std::endl
            << "B = " << raw_b_value << std::endl;

        std::cout << std::endl;
*/
        std::cout
//            << "Normalized" << std::endl
            << "R = " << round(raw_r_value / 256.0) << std::endl
            << "G = " << round(raw_g_value / 256.0) << std::endl
            << "B = " << round(raw_b_value / 256.0) << std::endl;



        return 0;

}
EN

回答 1

Code Review用户

发布于 2020-03-27 15:59:27

使用XGetPixel()而不是编写自己的函数

只要有可能,就应该使用X库提供的API。实现细节可能会随着时间的推移而改变,因此您自己的XGetPixel()副本在将来可能不再正确。当然,这在Xlib中是不太可能的,但总的来说,这是很好的实践。

不要不必要地经过const引用,

不要通过const引用传递小变量。您通常只在传递值的成本大于通过引用传递(例如std::stringstd::vector等)的重要类型时才这样做。对于像intfloat之类的东西,您不应该需要一个const引用。所以:

代码语言:javascript
复制
void MyGetPixelColor(Display *my_display, int x_coord, int y_coord, XColor *pixel_color)
{ ... }

适当时

按值返回

返回某些值的函数最好是return该值,而不是接受将被写入的指针。所以我会重写你的代码:

代码语言:javascript
复制
Xcolor MyGetPixelColor(Display *my_display, int x_coord, int y_coord)
{
    Xcolor pixel_color;
    XImage *screen_image = ...;
    pixel_color->pixel = XGetPixel(screen_image, 0, 0);
    XFree(screen_image);
    XQueryColor(my_display, XDefaultColormap(my_display, XDefaultScreen(my_display)), pixel_color);

    return pixel_color;
}

这不会导致Xcolor的额外副本,因为C++编译器将在这里使用返回值优化。

使用\n而不是std::endl

避免使用std::endl,它等同于\n,但它也会刷新输出,这可能会影响性能。有关更多详细信息,请参阅这个问题

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/239490

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档