I need to take a screenshot of the screen and save it to a folder. It is necessary to encode the resulting screenshot in base64 and pack it into Poco::JSON:Object.
I wrote the following code. I get a screenshot, but when I save it in jpeg format I get a black rectangle.
I also can't encode it in base64, my code crashes with exception:
JSON Exception [in file "/home/tania/.conan/data/poco/1.12.4/_/_/build/104aa542f7a964a7dcca5ea37969ea31f3c0ce48/src/Foundation/src/ErrorHandler.cpp", line 38]
file Screenshop.hpp:
namespace tasks::screenshot {
class Screenshot : public Poco::Runnable {
public:
explicit Screenshot(Poco::Logger &logger);
/**
A constructror. Construct a X11Screenshot object.
@param image a XImage pointer - image to process
@param new_width integer - change initial image width to new value, default is 0 - no resize
@param new_height integer - change initial image height to new value, default is 0 - no resize
@param scale_type string - type of interpolation for scaling, available "linear" and "bilinear", default is "linear"
*/
Screenshot(Poco::Logger &logger, XImage * image, int new_width=0, int new_height=0, std::string scale_type="linear");
/**
Public method to save image to jpeg file
@param path a constant character pointer - path where to create jpeg image
@param quality integer - level of jpeg compression, lower the value higher the compression
@return a boolean - true if succesfuly created image file, false otherwise
*/
bool save_to_jpeg(const char * path, int quality);
private:
void run() override;
/**
A private method to process XImage pixels to rgb bytes as is
@param image an XImage pointer - image to process to rgb char vector
@return vector of unsigned characters vectors - representing rgb values line by line
*/
std::vector<std::vector<unsigned char>> process_original(XImage * image);
/**
A private method to process XImage pixels to rgb bytes with
scale procesed by a lineral function (f(x) = ax + b)
@param image an XImage pointer - image to process to rgb char vector
@param new_width a integer - scale to this max width
@param new_height a integer - scale to this max height
@return vector of unsigned characters vectors - representing rgb values line by line
*/
std::vector<std::vector<unsigned char>> process_scale_linear(XImage * image, int new_width=0, int new_height=0);
/**
A private method to process XImage pixels to rgb bytes with
scale procesed by a bilineral function (f(x, y) = a0 + a1x + a2y + a3xy)
@param image an XImage pointer - image to process to rgb char vector
@param new_width a integer - scale to this max width
@param new_height a integer - scale to this max height
@return vector of unsigned characters vectors - representing rgb values line by line
*/
std::vector<std::vector<unsigned char>> process_scale_bilinear(XImage * image, int new_width=0, int new_height=0);
Poco::Logger &logger_;
// json парсер
Poco::JSON::Parser json_parser_{};
/**
A private integer variable width
Stores image current max width in pixels
*/
int width = 0;
/**
A private integer variable height
Stores image max height in pixels
*/
int height = 0;
/**
A private vector of unsigned characters vectors image_data
Contains rgb values of an image
*/
std::vector<std::vector<unsigned char>> image_data = std::vector<std::vector<unsigned char>>();
};
}
file Screenshot.cpp
#include "Screenshot.hpp"
namespace tasks::screenshot {
Screenshot::Screenshot(Poco::Logger &logger)
: logger_(logger)
{
}
Screenshot::Screenshot(Poco::Logger &logger, XImage * image, int new_width, int new_height, std::string scale_type)
: logger_(logger)
{
this->width = image->width;
this->height = image->height;
if ((new_width == 0 && new_height == 0) ||(new_width == this->width && new_height == this->height))
this->image_data = this->process_original(image);
else if (scale_type == "linear")
this->image_data = this->process_scale_linear(image, new_width, new_height);
else if (scale_type == "bilinear")
this->image_data = this->process_scale_bilinear(image, new_width, new_height);
else
throw std::invalid_argument("Invalid initialisation parameters.");
};
void Screenshot::run()
{
logger_.information("Run screenshot task");
Display* display = XOpenDisplay(nullptr);
Window root = DefaultRootWindow(display);
int width, height;
XWindowAttributes attributes = {0};
XGetWindowAttributes(display, root, &attributes);
width = attributes.width;
height = attributes.height;
XImage* img = XGetImage(display, root, 0, 0 , width, height, AllPlanes, ZPixmap);
logger_.information("получили скриншот экрана");
XDestroyImage(img);
XCloseDisplay(display);
// теперь сожмем изображение
int quality = 99;
this->width = width;
this->height = height;
this->image_data = this->process_original(img);
logger_.information("успешно выполнили сжатие изображения");
std::string path = fmt::format("/home/tania/CLionProjects/c++/HelloWorld/screenshots/{0}.jpeg","test");
if (this->save_to_jpeg(path.c_str(), quality)) {
std::cout << "Successfully saved to " << path << std::endl;
}
//подготовим отправку на бэк-сервер
std::stringstream string_stream;
if (!image_data.empty())
{
Poco::Base64Encoder encoder(string_stream);
encoder.write(reinterpret_cast<const char*> (image_data.data()),
static_cast<int64_t> (image_data.size()));
encoder.close();
}
logger_.information("приступим к парсингу в json: {0}",string_stream.str());
auto parsed_json = json_parser_.parse(string_stream.str());
logger_.information("упакуем в объект");
Poco::JSON::Object pobject = *parsed_json.extract<Poco::JSON::Object::Ptr>();
logger_.information(string_stream.str());
}
bool Screenshot::save_to_jpeg(const char * path, int quality)
{
FILE *fp = NULL;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPARRAY row;
fp = fopen(path, "wb");
if (!fp) {
std::cout << "Failed to create file " << path << std::endl;
return false;
}
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, fp);
cinfo.image_width = this->width;
cinfo.image_height = this->height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality (&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
for(std::vector<std::vector<unsigned char>>::size_type i = 0; i != this->image_data.size(); i++) {
row = (JSAMPARRAY) &this->image_data[i];
jpeg_write_scanlines(&cinfo, row, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
if (fp != NULL) fclose(fp);
return true;
};
std::vector<std::vector<unsigned char>> Screenshot::process_original(XImage * image) {
std::vector<std::vector<unsigned char>> image_data;
std::vector<unsigned char> image_data_row;
unsigned long red_mask = image->red_mask;
unsigned long green_mask = image->green_mask;
unsigned long blue_mask = image->blue_mask;
for (int y = 0; y < this->height; y++) {
for (int x = 0; x < this->width; x++) {
unsigned long pixel = XGetPixel(image, x, y);
unsigned char blue = pixel & blue_mask;
unsigned char green = (pixel & green_mask) >> 8;
unsigned char red = (pixel & red_mask) >> 16;
image_data_row.push_back(red);
image_data_row.push_back(green);
image_data_row.push_back(blue);
}
image_data.push_back(image_data_row);
image_data_row.clear();
}
return image_data;
};
std::vector<std::vector<unsigned char>> Screenshot::process_scale_linear(XImage * image, int new_width, int new_height){
std::vector<std::vector<unsigned char>> image_data;
std::vector<unsigned char> image_data_row;
unsigned long red_mask = image->red_mask;
unsigned long green_mask = image->green_mask;
unsigned long blue_mask = image->blue_mask;
float x_ratio = ((float) (this->width))/new_width;
float y_ratio = ((float) (this->height))/new_height;
for (int new_y=0; new_y < new_height; new_y++) {
for (int new_x=0; new_x < new_width; new_x++) {
unsigned long pixel = XGetPixel(image, (int) new_x * x_ratio, (int) new_y * y_ratio);
unsigned char blue = pixel & blue_mask;
unsigned char green = (pixel & green_mask) >> 8;
unsigned char red = (pixel & red_mask) >> 16;
image_data_row.push_back(red);
image_data_row.push_back(green);
image_data_row.push_back(blue);
}
image_data.push_back(image_data_row);
image_data_row.clear();
}
// update width and height after resize
this->width = new_width;
this->height = new_height;
return image_data;
};
std::vector<std::vector<unsigned char>> Screenshot::process_scale_bilinear(XImage * image, int new_width, int new_height){
std::vector<std::vector<unsigned char>> image_data;
std::vector<unsigned char> image_data_row;
float x_ratio = ((float) (this->width))/new_width;
float y_ratio = ((float) (this->height))/new_height;
unsigned long red_mask = image->red_mask;
unsigned long green_mask = image->green_mask;
unsigned long blue_mask = image->blue_mask;
for (int new_y=0; new_y < new_height; new_y++) {
for (int new_x=0; new_x < new_width; new_x++) {
// x1, y1 is coordinates original pixel to take from original image
// x2 is step to the right
//y2 is step down
int x_1 = new_x * x_ratio;
if (x_1 >= this->width) x_1 = this->width - 1; //becouse start pint is 0 and final is 1 less
int y_1 = new_y * y_ratio;
if(y_1 >= this->height) y_1 = this->height - 1; //becouse start pint is 0 and final is 1 less
int x_2 = x_1 + x_ratio;
if (x_2 >= this->width) x_2 = this->width - 1;
int y_2 = y_1 + y_ratio;
if(y_2 >= this->height) y_2 = this->height - 1;
float x_diff = (x_ratio * new_x) - x_1 ;
float y_diff = (y_ratio * new_y) - y_1 ;
// 4 point for bilineral function
unsigned long q_1_1 = XGetPixel(image, x_1, y_1);
unsigned long q_1_2 = XGetPixel(image, x_1, y_2);
unsigned long q_2_1 = XGetPixel(image, x_2, y_1);
unsigned long q_2_2 = XGetPixel(image, x_2, y_2);
// blue element
// Yb = Ab(1-w1)(1-h1) + Bb(w1)(1-h1) + Cb(h1)(1-w1) + Db(wh)
float blue = (q_1_1 & blue_mask) * (1 - x_diff) * (1 - y_diff) + (q_1_2 & blue_mask) * (x_diff) * (1 - y_diff) +
(q_2_1 & blue_mask) * (y_diff) * (1 - x_diff) + (q_2_2 & blue_mask) * (x_diff * y_diff);
// green element
// Yg = Ag(1-w1)(1-h1) + Bg(w1)(1-h1) + Cg(h1)(1-w1) + Dg(wh)
float green = ((q_1_1 & green_mask) >> 8) * (1-x_diff) * (1-y_diff) + ((q_1_2 & green_mask) >> 8) * (x_diff) * (1 - y_diff) +
((q_2_1 & green_mask) >> 8) * (y_diff) * (1-x_diff) + ((q_2_2 & green_mask) >> 8) * (x_diff * y_diff);
// red element
// Yr = Ar(1-w1)(1-h1) + Br(w1)(1-h1) + Cr(h1)(1-w1) + Dr(wh)
float red = ((q_1_1 & red_mask) >> 16) * (1 - x_diff) * (1 - y_diff) + ((q_1_2 & red_mask) >> 16) * (x_diff) * (1 - y_diff) +
((q_2_1 & red_mask) >> 16) * (y_diff) * (1 - x_diff) + ((q_2_2 & red_mask) >> 16) * (x_diff * y_diff);
image_data_row.push_back((int) red);
image_data_row.push_back((int) green);
image_data_row.push_back((int )blue);
}
image_data.push_back(image_data_row);
image_data_row.clear();
}
// update width and height after resize
this->width = new_width;
this->height = new_height;
return image_data;
};
}
fix code need in run() method. Please help me please.