日記帳

日記です。

X11 でスクリーンキャプチャー その2

32bpp, 16bpp, 8bpp, まで対応してみる.暇なのか?
しかし結局手元の環境で 8bpp の場合に正常な絵をキャプチャすることはできませんでしたとさ.

16bpp だとこんな絵が撮れます.

しかし 8bpp だとこんな絵になってしまいます.


問01 8bpp の絵が変なのは何が間違っているのでしょうか?

以下コード.

/************************************************************
 * gcc capture_screen_x11-02.c -L/usr/X11R6/lib -lX11
************************************************************/
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>

#define IMAGE_FILE_PATH "capture_screen_x11.ppm"

int mask2shiftbit(unsigned long mask)
{
	int result = -1;
	int i;
	const int bits = sizeof(unsigned long) * 8;
	const unsigned long msb = 1 << (bits-1);

	for (i = 0;i < bits;i++)
	{
		if (mask & msb)
		{
			result = bits - 8 - i;
			break;
		}
		mask <<= 1;
	}

	return result;
}

static
void write32bppXImageToP3File(XImage *image, const char *file_path)
{
	FILE *file;
	assert(image != NULL);
	assert(image->bits_per_pixel == 32);
	assert(file_path != NULL);

	file = fopen(file_path, "wb");
	if (file != NULL)
	{
		int x, y;
		uint8_t *datap;
		int red_shift, green_shift, blue_shift;

		red_shift = mask2shiftbit(image->red_mask);
		green_shift = mask2shiftbit(image->green_mask);
		blue_shift = mask2shiftbit(image->blue_mask);

		fprintf(file, "P3\n");
		fprintf(file, "%d %d\n", image->width, image->height);
		fprintf(file, "255\n");
		for (y = 0;y < image->height;y++)
		{
			datap = (uint8_t *)(image->data + y * image->bytes_per_line);
			for (x = 0;x < image->width;x++)
			{
				uint32_t pixel;

				pixel = *(uint32_t *)datap;
				fprintf(file, "%lu %lu %lu ",
					(pixel & image->red_mask) >> red_shift,
					(pixel & image->green_mask) >> green_shift,
					(pixel & image->blue_mask) >> blue_shift);
				datap += 4;
			}
			fprintf(file, "\n");
		}
		fclose(file);
	}
}

static
void write16bppXImageToP3File(XImage *image, const char *file_path)
{
	FILE *file;
	assert(image != NULL);
	assert(image->bits_per_pixel == 16);
	assert(file_path != NULL);

	file = fopen(file_path, "wb");
	if (file != NULL)
	{
		int x, y;
		uint8_t *datap;
		int red_shift, green_shift, blue_shift;

		red_shift = mask2shiftbit(image->red_mask);
		green_shift = mask2shiftbit(image->green_mask);
		blue_shift = mask2shiftbit(image->blue_mask);

		fprintf(file, "P3\n");
		fprintf(file, "%d %d\n", image->width, image->height);
		fprintf(file, "255\n");
		for (y = 0;y < image->height;y++)
		{
			datap = (uint8_t *)(image->data + y * image->bytes_per_line);
			for (x = 0;x < image->width;x++)
			{
				unsigned long r, g, b;
				uint16_t pixel;

				pixel = *(uint16_t *)datap;
				r = pixel & image->red_mask;
				g = pixel & image->green_mask;
				b = pixel & image->blue_mask;
				r = red_shift >= 0 ? r >> red_shift : r << -red_shift;
				g = green_shift >= 0 ? g >> green_shift : g << -green_shift;
				b = blue_shift >= 0 ? b >> blue_shift : b << -blue_shift;
				fprintf(file, "%lu %lu %lu ", r, g, b);
				datap += 2;
			}
			fprintf(file, "\n");
		}
		fclose(file);
	}
}

static
void write8bppXImageToP3File(XImage *image, XColor *colors, const char *file_path)
{
	FILE *file;
	assert(image != NULL);
	assert(image->bits_per_pixel == 8);
	assert(file_path != NULL);

	file = fopen(file_path, "wb");
	if (file != NULL)
	{
		int x, y;
		uint8_t *datap;

		fprintf(file, "P3\n");
		fprintf(file, "%d %d\n", image->width, image->height);
		fprintf(file, "255\n");

		for (y = 0;y < image->height;y++)
		{
			datap = ((uint8_t *)image->data + (y * image->bytes_per_line));
			for (x = 0;x < image->width;x++)
			{
				XColor color;
				uint8_t pixel;

				pixel = *datap;
				color = colors[pixel];
				fprintf(file, "%u %u %u ", color.red >> 8,
					color.green >> 8, color.blue >> 8);
				datap += 1;
			}
			fprintf(file, "\n");
			fflush(file);
		}
		fclose(file);
	}
}

int main(int argc, char* argv[])
{
	Display* display;
	int screen;
	Window rootWindow;
	XWindowAttributes attributes;
	int width, height;
	XImage *image;

	display = XOpenDisplay("");
	screen = DefaultScreen(display);
	rootWindow = RootWindow(display, screen);

	XGetWindowAttributes(display, rootWindow, &attributes);
	width = attributes.width;
	height = attributes.height;

	image = XGetImage(display, rootWindow,
		0, 0, width, height,
		AllPlanes, ZPixmap);

	if (image != NULL)
	{
		switch (image->bits_per_pixel)
		{
		case 32 :
			write32bppXImageToP3File(image, IMAGE_FILE_PATH);
			break;
		case 16 :
			write16bppXImageToP3File(image, IMAGE_FILE_PATH);
			break;
		case 8 :
			{
				int i, num_colors;;
				XColor *colors;

				num_colors = attributes.visual->map_entries;
				colors = (XColor *)malloc(sizeof(XColor) * num_colors);
				for (i = 0;i < num_colors;i++) colors[i].pixel = i;
				XQueryColors(display, attributes.colormap, colors, num_colors);
				write8bppXImageToP3File(image, colors, IMAGE_FILE_PATH);
				free(colors);
			}
			break;
		default:
			fprintf(stderr, "Not supported image : bits_per_pixel = %d\n", image->bits_per_pixel);
			break;
		}
		XFree(image);
	}
	
	XCloseDisplay(display);
	return 0;
}

以下試した環境.

CPU
Pentium 4 2.53GHz
GPU
NVIDIA GeForce4 MX 420
OS
Linux 2.6.16
X11
Xorg-6.9.0
X11のドライバ
nvidia


ところで Colormap を処理するかどうかの分岐は Visual class を見るべきなのかなぁ…?