Xlib image grab fails in XGetImage - c

Cookie cuttered (from supposedly working code) a trivial C program to perform a Xlib image grab using XGetImage(). At this point I'm not trying to process the image, this is just a proof-of-concept to see if the image grab works - and it doesn't. The XGetImage() call fails like:
X Error of failed request: BadMatch (invalid parameter attributes)
Major opcode of failed request: 73 (X_GetImage)
Serial number of failed request: 21
Current serial number in output stream: 21
I spent a fair amount of time researching this and apparently this problem has plagued other developers and no definitive answer was ever arrived at. Does someone know how I could go about resolving this? I can tell from the printf that the window of interest was correctly identified. The XMapRaised() is a suggestion from a prior thread on this problem, but doesn't seem to help. Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "/usr/include/X11/Xlib.h"
Window findScidWindow(Display *display )
{
Bool found = False;
Window rootWindow = RootWindow(display, DefaultScreen(display));
Atom atom = XInternAtom(display, "_NET_CLIENT_LIST", True);
Atom actualType;
int format;
unsigned long numItems;
unsigned long bytesAfter;
unsigned char *data = '\0';
Window *list;
char *windowName;
int status = XGetWindowProperty(display, rootWindow, atom, 0L, (~0L), False,
AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, &data);
list = (Window *)data;
if (status >= Success && numItems)
{
for (int i = 0; i < numItems; ++i)
{
status = XFetchName(display, list[i], &windowName);
if (status >= Success)
{
if(strstr(windowName, "Scid vs. PC") != NULL)
{
XFree(windowName);
XFree(data);
return list[i];
}
}
}
}
}
void
main( int argc, char*argv )
{
Display* d = XOpenDisplay(":0.0");
XImage *image;
Window root = (Window)0x0560003b; /* obtained via 'wmctrl -l -G' */
Window ScidWindow = findScidWindow(d);
XWindowAttributes attrib;
XGetWindowAttributes(d, ScidWindow, &attrib);
int width = attrib.width;
int height = attrib.height;
printf("width: %d height: %d\n",width,height);
XMapRaised(d, root);
/* coordinates 438,110 obtained via 'wmctrl -l -G' */
image = XGetImage( d, ScidWindow, 438, 110, width, height, AllPlanes, ZPixmap);
}

The issue is
image = XGetImage( d, ScidWindow, 438, 110, width, height, AllPlanes, ZPixmap);
uses x = 438 and y = 110 that is particular a problem if x + width is actually bigger as the window width (same for the height)
So here I have to assume you're not attempting to crop the window image but rather want to take a plain raw screenshot, then you just need to pass 0 for x and y:
image = XGetImage( d, ScidWindow, 0, 0, width, height, AllPlanes, ZPixmap);
The explanation is that the coordination system is not the full display or screen but the one of the window you are grabbing. Means the window starts at (0, 0).
This took me also some time to figure out.

Related

XGetImage is mangled for chrome, firefox, electron, when using XCompositeRedirectWindow

I am attempting to create a thumbnailer (for i3wm on linux) which generates an icon from a screenshot for all the available windows. The aim being to replicate something like how windows uses Alt-Tab to produce a pane of windows to choose from;
The current prototype uses pygtk to generate the dialog, and it creates thumbnails of the windows in the _NET_CLIENT_LIST using XGetImage where the windows have been redirected using XCompositeRedirectWindow. It works pretty well except for that images captured from windows which are browsers, (e.g. firefox, chrome, electron) are mangled, or wrong;
Basically, tools like xwd, scrot and import don't work for the hidden windows in i3, presumably because they are unmapped. So I have pulled some code together to create the thumbnails. The core of it is based on an example using XCompositeRedirectWindow from X11/extensions/Xcomposite.h from here.
My attempt at creating a short example is here;
https://gist.github.com/tolland/4bb1e97db258b92618adfb783ce66fac
it can be compiled with;
$ gcc example.c -lX11 -lXcomposite -lXrender -lpng -o example
and then to output png files for each of the windows;
$ mkdir -p /tmp/png_out && ./example
will produce pngs for each of the images
window found was 58720312
found window name for 58720312 : build : sudo
filename /tmp/png_out/58720312_test3.png
window found was 79691781
found window name for 79691781 : .xsession-errors - glogg
filename /tmp/png_out/79691781_test3.png
window found was 62914576
found window name for 62914576 : Edit - Stack Overflow - Mozilla Firefox
filename /tmp/png_out/62914576_test3.png
So the code I am currently using to walk the _NET_CLIENT_LIST is this;
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <libpng16/png.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xrender.h>
//Compile hint: gcc -shared -O3 -lX11 -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c
typedef int bool;
#define true 1
#define false 0
const int DEBUG = 0;
void getScreen2(const int, const int, const int, const int, const XID);
void write_png_for_image(XImage *image, XID xid, int width, int height,
char *filename);
typedef int (*handler)(Display *, XErrorEvent *);
XID getWindows(Display *display, Window parent, Window window, XID xid,
int depth);
int main() {
Display *display = XOpenDisplay(NULL);
Window root = DefaultRootWindow(display);
uint nwindows;
Window root_return, parent_return, *windows;
Atom a = XInternAtom(display, "_NET_CLIENT_LIST", true);
Atom actualType;
int format;
unsigned long numItems, bytesAfter;
unsigned char *data = 0;
int status = XGetWindowProperty(display, root, a, 0L, (~0L),
false,
AnyPropertyType, &actualType, &format, &numItems, &bytesAfter, &data);
char* window_name_return;
if (status >= Success && numItems) {
long *array = (long*) data;
for (long k = 0; k < numItems; k++) {
Window window = (Window) array[k];
//not finding chrome window name
printf("window found was %d \n", window);
if (XFetchName(display, window, &window_name_return)) {
printf("found window name for %d : %s \n", window,
window_name_return);
}
//XMapWindow(display, parent);
XMapWindow(display, window);
XWindowAttributes attr;
Status status = XGetWindowAttributes(display, window, &attr);
if (status == 0)
printf("Fail to get window attributes!\n");
getScreen2(0, 0, attr.width, attr.height, window);
}
XFree(data);
}
return 0;
}
which calls this function to map the window and call XCompositeRedirectWindow;
void getScreen2(const int xx, const int yy, const int W, const int H,
const XID xid) {
Display *display = XOpenDisplay(NULL);
Window root = DefaultRootWindow(display);
// turn on --sync to force error on correct method
//https://www.x.org/releases/X11R7.6/doc/man/man3/XSynchronize.3.xhtml
XSynchronize(display, True);
int counter = 1;
// select which xid to operate on, the winder or its parent
//XID xwid = fparent;
XID xwid = xid;
// Requests the X server to direct the hierarchy starting at window to off-screen storage
XCompositeRedirectWindow(display, xwid, CompositeRedirectAutomatic);
XWindowAttributes attr;
Status status = XGetWindowAttributes(display, xwid, &attr);
int width = attr.width;
int height = attr.height;
int depth = attr.depth;
Pixmap xc_pixmap = XCompositeNameWindowPixmap(display, xwid);
if (!xc_pixmap) {
printf("xc_pixmap not found\n");
}
//XWriteBitmapFile(display, "test1.xpm", pixmap, W, H, -1, -1);
XRenderPictFormat *format = XRenderFindVisualFormat(display, attr.visual);
XRenderPictureAttributes pa;
pa.subwindow_mode = IncludeInferiors;
Picture picture = XRenderCreatePicture(display, xwid, format,
CPSubwindowMode, &pa);
char buffer[50];
int n;
int file_counter = 1;
n = sprintf(buffer, "/tmp/%d_test%d.xpm", xid, file_counter++);
XWriteBitmapFile(display, buffer, xc_pixmap, W, H, -1, -1);
n = sprintf(buffer, "/tmp/%d_test%d.xpm", xid, file_counter++);
XWriteBitmapFile(display, buffer, xid, W, H, -1, -1);
XImage *image = XGetImage(display, xid, 0, 0, W, H, AllPlanes, ZPixmap);
if (!image) {
printf("XGetImage failed\n");
}
char filename[255];
int n2;
n2 = sprintf(filename, "/tmp/png_out/%d_test%d.png", xid, file_counter++);
printf("filename %s \n", filename);
write_png_for_image(image, xid, W, H, filename);
//XFree(image);
XDestroyImage(image);
XDestroyWindow(display, root);
XCloseDisplay(display);
}
and then this to write out the png;
void write_png_for_image(XImage *image, XID xid, int width, int height,
char *filename) {
int code = 0;
FILE *fp;
png_structp png_ptr;
png_infop png_info_ptr;
png_bytep png_row;
char buffer[50];
int n;
n = sprintf(buffer, filename, xid);
// Open file
fp = fopen(buffer, "wb");
if (fp == NULL) {
fprintf(stderr, "Could not open file for writing\n");
code = 1;
}
// Initialize write structure
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fprintf(stderr, "Could not allocate write struct\n");
code = 1;
}
// Initialize info structure
png_info_ptr = png_create_info_struct(png_ptr);
if (png_info_ptr == NULL) {
fprintf(stderr, "Could not allocate info struct\n");
code = 1;
}
// Setup Exception handling
if (setjmp(png_jmpbuf (png_ptr))) {
fprintf(stderr, "Error during png creation\n");
code = 1;
}
png_init_io(png_ptr, fp);
// Write header (8 bit colour depth)
png_set_IHDR(png_ptr, png_info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
// Set title
char *title = "Screenshot";
if (title != NULL) {
png_text title_text;
title_text.compression = PNG_TEXT_COMPRESSION_NONE;
title_text.key = "Title";
title_text.text = title;
png_set_text(png_ptr, png_info_ptr, &title_text, 1);
}
png_write_info(png_ptr, png_info_ptr);
// Allocate memory for one row (3 bytes per pixel - RGB)
png_row = (png_bytep) malloc(3 * width * sizeof(png_byte));
unsigned long red_mask = image->red_mask;
unsigned long green_mask = image->green_mask;
unsigned long blue_mask = image->blue_mask;
// Write image data
//int xxx, yyy;
for (int y = 0; y < height; y++) {
for (int x = 0; x < 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;
png_byte *ptr = &(png_row[x * 3]);
ptr[0] = red;
ptr[1] = green;
ptr[2] = blue;
}
png_write_row(png_ptr, png_row);
}
// End write
png_write_end(png_ptr, NULL);
// Free
fclose(fp);
if (png_info_ptr != NULL)
png_free_data(png_ptr, png_info_ptr, PNG_FREE_ALL, -1);
if (png_ptr != NULL)
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
if (png_row != NULL)
free(png_row);
}
However when the windows is browser based, the image is mangled like so;
Do I need to do something special to get browser windows working?
References;
https://www.talisman.org/~erlkonig/misc/x11-composite-tutorial/
Get a screenshot of a window that is cover or not visible or minimized with Xcomposite extension for X11

How can i add line breaks when rendering Text using X11,

I am making an application that renders texts according to style mentioned on screen using X Windows System and Xft. My code is working fine as shown below.
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
char * nowShowing()
{
return strdup("This is a sample text This is rendered with new Driver installed This is a sample text his is rendered with new Driver installed");
}
int main()
{
XftFont *font;
XftDraw *draw;
XRenderColor render_color;
XftColor xft_color;
char *str;
str = nowShowing();
int x = 70;
int y = 150;
Display *dis = XOpenDisplay (0);
int screen = DefaultScreen (dis);
Window w = XCreateSimpleWindow (dis, RootWindow (dis, screen),
0, 0, 1200, 300, 1,
BlackPixel (dis, screen),
WhitePixel (dis, screen));
XEvent ev;
render_color.red = 0;
render_color.green =0;
render_color.blue = 0;
render_color.alpha = 0xffff;
XftColorAllocValue (dis,
DefaultVisual(dis, screen),
DefaultColormap(dis, screen),
&render_color,
&xft_color);
//font = XftFontOpen(dis, screen,
// XFT_FAMILY, XftTypeString, "charter",
// XFT_SIZE, XftTypeDouble, 20.0,
// NULL);
font = XftFontOpenName(dis,screen,"URW Palladio L:style=Bold Italic"); //it takes a Fontconfig pattern string
draw = XftDrawCreate(dis, w,
DefaultVisual(dis, screen),
DefaultColormap(dis, screen));
XSelectInput (dis, w, ExposureMask);
XMapWindow (dis, w);
for (;;)
{
XNextEvent (dis, &ev);
if (ev.type == Expose)
XftDrawString8(draw, &xft_color, font, x, y, (XftChar8 *) str,
strlen(str));
}
return 0;
}
But i am wondering that how can i add line breaks in the text entered. I tried using "/n" and also tried to make array and used loops but it didn't work.
New line "\n" will not be rendered by Xft. You need to render each line separately with proper offset, depending on font size and desired spacing.
I have modified the ending block of your code with sample text rendered two times on separate lines.
if (ev.type == Expose)
{
int fonth = font->ascent + font->descent;
XftDrawString8(draw, &xft_color, font, x, y, (XftChar8 *) str,
strlen(str));
XftDrawString8(draw, &xft_color, font, x, y+fonth, (XftChar8 *) str,
strlen(str));
}

How to directly query the camera about image luminance/ Skip compentation in OpenCV

I am developing a program in VS 2010 using OpenCV. I want to measure the luminance of every frame that the computer's camera captures. However, the camera's software stabilizes the luminance after 2-3 frames. Eg, if i put my thumb in front of the camera the first frame's luminance is 2 (scale from 0 to 255), but then while keeping my thumb in front of the camera the luminance becomes 7 and the 20 - it is stabilized there for the next frames. So the camera tries to make too dark pictures brighter and too bright pictures darker.
How can i measure the actual luminance without the camera's interference?
My code is:
#ifdef _CH_
#pragma package <opencv>
#endif
#include "stdafx.h"
#include <highgui.h>
#include "cv.h"
#include <stdio.h>
#include <stdlib.h>
#include "..\utilities.h"
int _tmain(int argc, _TCHAR* argv[])
{
FILE *file;
IplImage *img;
IplImage* grayscale_image;
int c, i, j, Luminance = 0, Pixel_Num = 0;
int Avg_Luminance;
int width_step;
int pixel_step;
// allocate memory for an image
// capture from video device #1
CvCapture* capture;
// create a window to display the images
cvNamedWindow("mainWin", CV_WINDOW_AUTOSIZE);
// position the window
cvMoveWindow("mainWin", 5, 5);
while(1)
{
if(file = fopen("luminance_value.txt", "w+"))
{
// retrieve the captured frame
capture= cvCaptureFromCAM(1);
img=cvQueryFrame(capture);
grayscale_image = cvCreateImage( cvGetSize(img), 8, 1 );
cvConvertImage( img, grayscale_image );
width_step= grayscale_image->widthStep;
pixel_step= grayscale_image->widthStep/grayscale_image->width;
Pixel_Num = grayscale_image->width * grayscale_image->height;
for(i = 0; i < grayscale_image->height; i++)
{
for(j = 0; j < grayscale_image->width; j++)
{
unsigned char* point = GETPIXELPTRMACRO( grayscale_image, j, i, width_step, pixel_step);
Luminance += point[0];
}
}
Avg_Luminance = Luminance / Pixel_Num;
//Avg_Luminance = cvGetCaptureProperty(capture,CV_CAP_PROP_BRIGHTNESS);
//file = fopen("luminance_value.txt", "w+");
fprintf(file, "%d", Avg_Luminance);
fclose(file);
printf("Avg_Luminance = %d\n", Avg_Luminance);
Luminance = 0;
Pixel_Num = 0;
// show the image in the window
cvShowImage("mainWin", grayscale_image );
cvReleaseCapture(&capture);
// wait 10 ms for a key to be pressed
c=cvWaitKey(10000);
// escape key terminates program
if(c == 27)
break;
}
else
{
continue;
}
}
return 0;
}

SDL: Unable to flip multiple surfaces

I've been trying to flip surfaces and have been successful if I'm only flipping a single surface (the same surface I got back from SDL_SetVideoMode). If I try to flip the surface I get back from SDL_DisplayFormat, nothing happens. I've attached demo code that demonstrates my problem:
#include <stdio.h>
#include <stdlib.h>
#include "SDL/SDL.h"
void main()
{
int i;
SDL_Surface *mysurface1;
SDL_Surface *mysurface2;
char *pxl;
SDL_Init( SDL_INIT_EVERYTHING );
mysurface1 = SDL_SetVideoMode( 640, 480, 8, SDL_DOUBLEBUF|SDL_HWSURFACE );
for (i = 0; i < 20; i++)
{
pxl = (char *)mysurface1->pixels + i*mysurface1->pitch + i;
*pxl = 100; // Red Line
}
SDL_Flip(mysurface1); // Works, we see a red line
sleep(5);
printf("Sleeping for 5...\n");
mysurface2 = SDL_DisplayFormat(mysurface1);
for (i = 0; i < 20; i++)
{
pxl = (char *)mysurface2->pixels + i*mysurface2->pitch + i;
*pxl = 255; // White line
}
SDL_Flip(mysurface2); // White line doesnt appear
printf("Done... No white line\n");
sleep(10);
}
Has anyone ever seen this before? Again, I think I tracked it down to surfaces that wont display if its a surface I got back from SDL_DisplayFormat. If I do it on the surface I get back from SDL_SetVideoMode, then I see the red line and everything works fine.
You can only flip the main display surface (the one created with SDL_SetVideoMode). In order to make your other surface visible, you need to blit it onto the main surface. Lookup SDL_BlitSurface for details on how to do that.
Pass the screen to the SDL_Flip function. The flip function modifies the value of screen->pixels so that it points to the surface that isn't visible on the screen.
However, this is only applicable to video devices such as SVGA and DGA. On X11, calling SDL_Flip(screen) is equivalent to calling SDL_UpdateRect(screen, 0, 0, 0, 0).
#include <stdio.h>
#include <stdlib.h>
#include "SDL/SDL.h"
void main()
{
int i;
SDL_Surface *screen;
char *pxl;
SDL_Init( SDL_INIT_EVERYTHING );
screen = SDL_SetVideoMode( 640, 480, 8, SDL_DOUBLEBUF|SDL_HWSURFACE );
printf("Drawing the red line ...\n");
printf("screen->pixels = %p\n", screen->pixels);
for (i = 0; i < 100; i++)
{
pxl = (char *)screen->pixels + i*screen->pitch + i;
*pxl = 100; // Red Line
}
printf("Flip screens\n");
SDL_Flip(screen); // Display the red line
printf("Drawing the white line ...\n");
printf("screen->pixels = %p\n", screen->pixels);
for (i = 0; i < 100; i++)
{
pxl = (char *)screen->pixels + i*screen->pitch + i;
*pxl = 255; // White line
}
sleep(3);
printf("Flip screens\n");
SDL_Flip(screen); // Display the white line
sleep(10);
}
On my Linux notebook, this prints:
Drawing the red line ...
screen->pixels = 0xb6c8c008
Flip screens
Drawing the white line ...
screen->pixels = 0xb6c8c008
Flip screens
The value of screen->pixels is the same, but this is only because on X11 the flip operation is a no-operation. On a video device such as SVGA or DGA, the two values would be different.
First, it seems SDL_Flip() only works on surfaces that correspond to the screen or a window, like those created by SDL_SetVideoMode(). Your other surface is off-screen; it doesn't make much sense to double-buffer it (or flip it), and it most likely isn't double-buffered anyway. Being an off screen surface, it won't appear until you blit it to your display surface with SDL_BlitSurface() or a similar function -- then, the changes will be visible next time you flip the display surface.
Essentially, mysurface2 isn't actually on your display until you put it there, by blitting it onto a surface that is on your display. If you replace the following:
SDL_Flip(mysurface2); // White line doesnt appear
With this:
SDL_BlitSurface(mysurface2,NULL,mysurface1,NULL);
SDL_Flip(mysurface1);
...then your code will probably work as you expect.

C - Convert GIF to JPG

I need to convert a GIF image to Jpeg image using C programming language. I searched the web, but I didn't find an example which could help me. Any suggestion are appreciated!
EDIT: I want to do this using an cross-platform open-source library like SDL.
Try the GD or ImageMagick libraries
I found libafterimage to be incredibly simple to use.
In this snippet I also scale the image to at most width or at most height, while preserving aspect:
#include <libAfterImage/afterimage.h>
int convert_image_to_jpeg_of_size(const char* infile, const char* outfile, const double max_width, const double max_height)
{
ASImage* im;
ASVisual* asv;
ASImage* scaled_im;
double height;
double width;
double pixelzoom;
double proportion;
im = file2ASImage(infile, 0xFFFFFFFF, SCREEN_GAMMA, 0, ".", NULL);
if (!im) {
return 1;
}
proportion = (double)im->width / (double)im->height;
asv = create_asvisual(NULL, 0, 0, NULL);
if (proportion > 1) {
/* Oblong. */
width = max_width;
pixelzoom = max_width / im->width;
height = (double)im->height * pixelzoom;
} else {
height = max_height;
pixelzoom = max_height / im->height;
width = (double)im->width * pixelzoom;
}
scaled_im = scale_asimage(asv, im, width, height, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
/* writing result into the file */
ASImage2file(scaled_im, NULL, outfile, ASIT_Jpeg, NULL);
destroy_asimage(&scaled_im);
destroy_asimage(&im);
return 0;
}
Not the easiest to use, but the fastest way is almost surely using libavcodec/libavformat from ffmpeg.

Resources