Example #1
0
pictw_t *
simg_load_icon(session_t *ps, Window wid, int desired_size) {
	pictw_t *pictw = NULL;
	bool processed = false;

	{
		// _NET_WM_ICON
		int best_width = 0, best_height = 0;
		float best_scale = 1.0f, best_area = 0.0f;
		const unsigned char *best_data = NULL;
		winprop_t prop = wid_get_prop_adv(ps, wid, _NET_WM_ICON, 0, ICON_PROP_MAXLEN, XA_CARDINAL, 32);
		if (prop.nitems) {
			int width = 0, height = 0;
			const long *end = prop.data32 + prop.nitems;
			// Format: WIDTH HEIGHT DATA (32-bit)
			int wanted_bytes = 0;
			for (const long *p = prop.data32; p < end; p += wanted_bytes) {
				if (p + 2 >= end) {
					printfef("(%#010lx): %d trailing byte(s).", wid, (int) (end - p));
					break;
				}
				width = p[0];
				height = p[1];
				if (width <= 0 || height <= 0) {
					printfef("(%#010lx): (offset %d, width %d, height %d) Invalid width/height.",
							wid, (int) (p - prop.data32), width, height);
					break;
				}
				wanted_bytes = 2 + width * height;
				if ((end - p) < wanted_bytes) {
					printfef("(%#010lx): (offset %d, width %d, height %d) Not enough bytes (%d/%d).",
							wid, (int) (p - prop.data32), width, height, (int) (end - p), wanted_bytes);
					break;
				}
				// Prefer larger ones if possible
				if (best_width >= desired_size && best_height >= desired_size
						&& (width < desired_size || height < desired_size))
					continue;
				float scale = MAX(1.0f,
						MIN((float) best_height / height, (float) best_width / width));
				float area = width * height * scale * scale;
				if (area > best_area) {
					best_width = width;
					best_height = height;
					best_scale = scale;
					best_area = area;
					best_data = (const unsigned char *) (p + 2);
				}
			}
		}
		if (best_data) {
			{
				unsigned char *converted_data = simg_data32_from_long(
						(const long *) best_data, best_width * best_height);
				simg_data32_premultiply(converted_data, best_width * best_height);
				pictw = simg_data_to_pictw(ps, best_width, best_height, 32, converted_data, 0);
				if (converted_data != best_data)
					free(converted_data);
			}
			if (!pictw)
				printfef("(%#010lx): Failed to create picture.", wid);
			/* if (pictw)
				printfdf("(%#010lx): (offset %d, width %d, height %d) Loaded.",
						wid, (int) (best_data - prop.data8), pictw->width, pictw->height); */
		}
		free_winprop(&prop);
	}

	if (pictw) goto simg_load_icon_end;

	// WM_HINTS
	// Our method probably fills 1-8 bit pixmaps as black instead of using
	// "suitable background/foreground". I hope it isn't a problem, though.
	{
		XWMHints *h = XGetWMHints(ps->dpy, wid);
		if (h && (IconPixmapHint & h->flags) && h->icon_pixmap)
			pictw = simg_pixmap_to_pictw(ps, 0, 0, 0, h->icon_pixmap, h->icon_mask);
		sxfree(h);
	}

	if (pictw) goto simg_load_icon_end;

	// KWM_WIN_ICON
	// Same issue as above.
	{
		winprop_t prop = wid_get_prop_adv(ps, wid, KWM_WIN_ICON, 0, 2, KWM_WIN_ICON, 32);
		if (prop.nitems) {
			Pixmap pxmap = prop.data32[0],
						 mask = (prop.nitems >= 2 ? prop.data32[1]: None);
			if (pxmap)
				pictw = simg_pixmap_to_pictw(ps, 0, 0, 0, pxmap, mask);
		}
		free_winprop(&prop);
	}

	if (pictw) goto simg_load_icon_end;

simg_load_icon_end:
	// Post-processing
	if (pictw && !processed) {
		pictw = simg_postprocess(ps, pictw, PICTPOSP_SCALEK,
				desired_size, desired_size, ALIGN_MID, ALIGN_MID, NULL);
		/* printfdf("(%#010lx): (width %d, height %d) Processed.",
				wid, pictw->width, pictw->height); */
	}

	return pictw;
}
Example #2
0
pictw_t *
sjpg_read(session_t *ps, const char *path) {
    pictw_t *pictw = NULL;
    JSAMPLE *data = NULL;
    bool need_abort = false;
    struct jpeg_error_mgr jerr;
    struct jpeg_decompress_struct cinfo = {
        .err = jpeg_std_error(&jerr),
    };
    jpeg_create_decompress(&cinfo);

    FILE *fp = fopen(path, "rb");
    if (unlikely(!fp)) {
        printfef("(\"%s\"): Failed to open file.", path);
        goto sjpg_read_end;
    }
    jpeg_stdio_src(&cinfo, fp);
    jpeg_read_header(&cinfo, TRUE);
    jpeg_start_decompress(&cinfo);
    need_abort = true;
    int width = 0, height = 0, depth = 24;
    {
        const int comps = 4;
        data = allocchk(malloc(cinfo.output_width *
                               cinfo.output_height * comps * sizeof(JSAMPLE)));
        JSAMPROW rowptrs[cinfo.output_height];
        for (int i = 0; i < cinfo.output_height; ++i)
            rowptrs[i] = data + i * cinfo.output_width * comps;
        width = cinfo.output_width;
        height = cinfo.output_height;
        // libjpeg enforces a loop to read scanlines
        while (cinfo.output_scanline < cinfo.output_height) {
            if (unlikely(!jpeg_read_scanlines(&cinfo, &rowptrs[cinfo.output_scanline],
                                              cinfo.output_height - cinfo.output_scanline))) {
                printfef("(\"%s\"): Failed to read scanline %d.", path,
                         cinfo.output_scanline);
                goto sjpg_read_end;
            }
        }

        // Expand greyscale value to RGBA
        if (1 == cinfo.output_components) {
            for (int i = 0; i < height; ++i)
                for (int j = width - 1; j >= 0; --j) {
                    unsigned char a = rowptrs[i][j];
                    rowptrs[i][j * comps + 0] = a;
                    rowptrs[i][j * comps + 1] = a;
                    rowptrs[i][j * comps + 2] = a;
                    rowptrs[i][j * comps + 3] = 0xff;
                }
        }
        else if (3 == cinfo.output_components) {
            for (int i = 0; i < height; ++i) {
                simg_data24_fillalpha(&data[i * 4 * width], width);
                simg_data24_tobgr(&data[i * 4 * width], width);
            }
        }
    }
    if (unlikely(!jpeg_finish_decompress(&cinfo))) {
        printfef("(\"%s\"): Failed to finish decompression.", path);
        goto sjpg_read_end;
    }
    need_abort = false;
    pictw = simg_data_to_pictw(ps, width, height, depth, data, 0);
    free(data);
    if (unlikely(!pictw)) {
        printfef("(\"%s\"): Failed to create Picture.", path);
        goto sjpg_read_end;
    }

sjpg_read_end:
    /* if (unlikely(data && !pictw))
    	free(data); */
    if (unlikely(need_abort))
        jpeg_abort_decompress(&cinfo);
    if (likely(fp))
        fclose(fp);
    jpeg_destroy_decompress(&cinfo);

    return pictw;
}
Example #3
0
int
clientwin_handle(ClientWin *cw, XEvent *ev) {
	MainWin *mw = cw->mainwin;
	session_t *ps = mw->ps;

	if (ev->type == ButtonRelease) {
		const unsigned button = ev->xbutton.button;
		if (cw->mainwin->pressed_mouse) {
			if (button < MAX_MOUSE_BUTTONS) {
				int ret = clientwin_action(cw,
						ps->o.bindings_miwMouse[button]);
				if (ret) {
                                    printfef("(): Quitting.");
                                    return ret;
				}
			}
		}
		else
			printfef("(): ButtonRelease %u ignored.", button);
	} else if (ev->type == KeyRelease) {
		if (cw->mainwin->pressed_key) {
			if (ev->xkey.keycode == cw->mainwin->key_up ||
					ev->xkey.keycode == cw->mainwin->key_k)
				focus_up(cw);
			else if (ev->xkey.keycode == cw->mainwin->key_down ||
					ev->xkey.keycode == cw->mainwin->key_j)
				focus_down(cw);
			else if (ev->xkey.keycode == cw->mainwin->key_left ||
					ev->xkey.keycode == cw->mainwin->key_h)
				focus_left(cw);
			else if (ev->xkey.keycode == cw->mainwin->key_right ||
					ev->xkey.keycode == cw->mainwin->key_l)
				focus_right(cw);
			else if (ev->xkey.keycode == cw->mainwin->key_enter
					|| ev->xkey.keycode == cw->mainwin->key_space) {
				childwin_focus(cw);
				return 1;
			}
			else
				report_key_unbinded(ev);
		}
		else
			report_key_ignored(ev);
	}
	else if (ev->type == ButtonPress) {
		cw->mainwin->pressed_mouse = true;
		/* if (ev->xbutton.button == 1)
			cw->mainwin->pressed = cw; */
	}
	else if (KeyPress == ev->type) {
		cw->mainwin->pressed_key = true;
	}
	else if(ev->type == FocusIn) {
		cw->focused = 1;
		clientwin_render(cw);
		XFlush(ps->dpy);
	} else if(ev->type == FocusOut) {
		cw->focused = 0;
		clientwin_render(cw);
		XFlush(ps->dpy);
	} else if(ev->type == EnterNotify) {
		XSetInputFocus(ps->dpy, cw->mini.window, RevertToParent, CurrentTime);
		if (cw->mainwin->tooltip) {
			int win_title_len = 0;
			FcChar8 *win_title = wm_get_window_title(ps, cw->wid_client, &win_title_len);
			if (win_title) {
				tooltip_map(cw->mainwin->tooltip,
						ev->xcrossing.x_root, ev->xcrossing.y_root,
						win_title, win_title_len);
				free(win_title);
			}
		}
	} else if(ev->type == LeaveNotify) {
		// XSetInputFocus(ps->dpy, mw->window, RevertToParent, CurrentTime);
		if(cw->mainwin->tooltip)
			tooltip_unmap(cw->mainwin->tooltip);
	}
	return 0;
}
Example #4
0
pictw_t *
sgif_read(session_t *ps, const char *path) {
	assert(path);
	pictw_t *pictw = NULL;
	GifPixelType *data = NULL;
	unsigned char *tdata = NULL;

	GifRecordType rectype;
	int ret = 0, err = 0;
	const char *errstr = NULL;
#ifdef SGIF_THREADSAFE
	GifFileType *f = DGifOpenFileName(path, &err);
#else
	GifFileType *f = DGifOpenFileName(path);
#endif
	if (unlikely(!f)) {
#ifdef SGIF_HAS_ERROR
		err = GifError();
		errstr = GifErrorString();
#endif
#ifdef SGIF_THREADSAFE
		errstr = GifErrorString(err);
#endif
		printfef("(\"%s\"): Failed to open file: %d (%s)", path, err, errstr);
		goto sgif_read_end;
	}

	int width = 0, height = 0, transp = -1;
	{
		int i = 0;
		while (GIF_OK == (ret = DGifGetRecordType(f, &rectype))
				&& TERMINATE_RECORD_TYPE != rectype) {
			++i;
			switch (rectype) {
				case UNDEFINED_RECORD_TYPE:
					printfef("(\"%s\"): %d: Encountered a record of unknown type.",
							path, i);
					break;
				case SCREEN_DESC_RECORD_TYPE:
					printfef("(\"%s\"): %d: Encountered a record of "
							"ScreenDescRecordType. This shouldn't happen!",
							path, i);
					break;
				case IMAGE_DESC_RECORD_TYPE:
					if (data) {
						printfef("(\"%s\"): %d: Extra image section ignored.",
								path, i);
						break;
					}
					if (GIF_ERROR == DGifGetImageDesc(f)) {
						printfef("(\"%s\"): %d: Failed to read GIF image info.",
								path, i);
						break;
					}
					width = f->Image.Width;
					height = f->Image.Height;
					if (width <= 0 || height <= 0) {
						printfef("(\"%s\"): %d: Width/height invalid.", path, i);
						break;
					}
					assert(!data);
					data = allocchk(malloc(width * height * sizeof(GifPixelType)));
					// FIXME: Interlace images may need special treatments
					for (int j = 0; j < height; ++j)
						if (GIF_OK != DGifGetLine(f, &data[j * width], width)) {
							printfef("(\"%s\"): %d: Failed to read line %d.", path, i, j);
							goto sgif_read_end;
						}
					break;
				case EXTENSION_RECORD_TYPE:
					{
						int code = 0;
						GifByteType *pbytes = NULL;
						if (GIF_OK != DGifGetExtension(f, &code, &pbytes) || !pbytes) {
							printfef("(\"%s\"): %d: Failed to read extension block.",
									path, i);
							break;
						}
						do {
							// Transparency
							if (0xf9 == code && (pbytes[1] & 1))
								transp = pbytes[4];
						} while (GIF_OK == DGifGetExtensionNext(f, &pbytes) && pbytes);
					}
					break;
				case TERMINATE_RECORD_TYPE:
					assert(0);
					break;
			}
		}
		if (unlikely(!data)) {
			printfef("(\"%s\"): No valid data found.", path);
			goto sgif_read_end;
		}
	}

	// Colormap translation
	int depth = 32;
	{
		ColorMapObject *cmap = f->Image.ColorMap;
		if (!cmap) cmap = f->SColorMap;
		if (unlikely(!cmap)) {
			printfef("(\"%s\"): No colormap found.", path);
			goto sgif_read_end;
		}
		tdata = allocchk(malloc(width * height * depth / 8));
		{
			GifPixelType *pd = data;
			unsigned char *end = tdata + width * height * depth / 8;
			for (unsigned char *p = tdata; p < end; p += depth / 8, ++pd) {
				// When the alpha is 0, X seemingly wants all color channels
				// to be 0 as well.
				if (transp >= 0 && transp == *pd) {
					p[0] = p[1] = p[2] = 0;
					if (32 == depth) p[3] = 0;
					continue;
				}
				p[0] = cmap->Colors[*pd].Blue;
				p[1] = cmap->Colors[*pd].Green;
				p[2] = cmap->Colors[*pd].Red;
				p[3] = 0xff;
			}
		}
	}
	if (unlikely(!(pictw = simg_data_to_pictw(ps, width, height, depth,
						tdata, 0)))) {
		printfef("(\"%s\"): Failed to create Picture.", path);
		goto sgif_read_end;
	}

sgif_read_end:
	if (data)
		free(data);
	if (likely(f))
		DGifCloseFile(f);

	return pictw;
}
Example #5
0
pictw_t *
spng_read(session_t *ps, const char *path) {
	assert(path);

	char sig[SPNG_SIGBYTES] = "";
	pictw_t *pictw = NULL;
	png_structp png_ptr = NULL;
	png_infop info_ptr = NULL;
	FILE *fp = fopen(path, "rb");
	bool need_premultiply = false;
	if (unlikely(!fp)) {
		printfef("(\"%s\"): Failed to open file.", path);
		goto spng_read_end;
	}
	if (unlikely(SPNG_SIGBYTES != fread(&sig, 1, SPNG_SIGBYTES, fp))) {
		printfef("(\"%s\"): Failed to read %d-byte signature.",
				path, SPNG_SIGBYTES);
		goto spng_read_end;
	}
	if (unlikely(png_sig_cmp((png_bytep) sig, 0, SPNG_SIGBYTES))) {
		printfef("(\"%s\"): PNG signature invalid.", path);
		goto spng_read_end;
	}
	png_ptr = allocchk(png_create_read_struct(PNG_LIBPNG_VER_STRING,
				NULL, NULL, NULL));
	info_ptr = allocchk(png_create_info_struct(png_ptr));
	if (setjmp(png_jmpbuf(png_ptr)))
		goto spng_read_end;
	png_init_io(png_ptr, fp);
	png_set_sig_bytes(png_ptr, SPNG_SIGBYTES);
	png_read_info(png_ptr, info_ptr);
	png_uint_32 width = 0, height = 0;

	// Set transformations
	int bit_depth = 0, color_type = 0;
	{
		int interlace_type = 0;
		png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
				&color_type, &interlace_type, NULL, NULL);

		// Scale or strip 16-bit colors
		if (bit_depth == 16) {
			printfdf("(\"%s\"): Scaling 16-bit colors.", path);
#if PNG_LIBPNG_VER >= 10504
			png_set_scale_16(png_ptr);
#else
			png_set_strip_16(png_ptr);
#endif
			bit_depth = 8;
		}

		/* if (bit_depth < 8)
			png_set_packing(png_ptr); */

		// No idea why this is needed...
		png_set_bgr(png_ptr);

		// Convert palette to RGB
		if (color_type == PNG_COLOR_TYPE_PALETTE) {
			printfdf("(\"%s\"): Converting palette PNG to RGB.", path);
			png_set_palette_to_rgb(png_ptr);
			color_type = PNG_COLOR_TYPE_RGB;
		}

		if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
			printfdf("(\"%s\"): Converting rDNS to full alpha.", path);
			png_set_tRNS_to_alpha(png_ptr);
		}

		if (color_type == PNG_COLOR_TYPE_GRAY
				|| PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
			printfdf("(\"%s\"): Converting gray (+ alpha) PNG to RGB.", path);
			png_set_gray_to_rgb(png_ptr);
			if (PNG_COLOR_TYPE_GRAY == color_type)
				color_type = PNG_COLOR_TYPE_RGB;
			else
				color_type = PNG_COLOR_TYPE_RGB_ALPHA;
		}

		/*
		if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
			printfdf("(\"%s\"): Converting 1/2/4 bit gray PNG to 8-bit.", path);
#if PNG_LIBPNG_VER >= 10209
			png_set_expand_gray_1_2_4_to_8(png_ptr);
#else
			png_set_gray_1_2_4_to_8(png_ptr);
#endif
			bit_depth = 8;
		}
		*/

		// Somehow XImage requires 24-bit visual to use 32 bits per pixel
		if (color_type == PNG_COLOR_TYPE_RGB) {
			printfdf("(\"%s\"): Appending filler alpha values.", path);
			png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
		}

		// Premultiply alpha
		if (PNG_COLOR_TYPE_RGB_ALPHA == color_type) {
#if PNG_LIBPNG_VER >= 10504
			png_set_alpha_mode(png_ptr, PNG_ALPHA_STANDARD, 1.0);
#else
			need_premultiply = true;
#endif
		}

		/*
		int number_passes = 1;
#ifdef PNG_READ_INTERLACING_SUPPORTED
		number_passes = png_set_interlace_handling(png_ptr);
#endif */

		if (PNG_INTERLACE_NONE != interlace_type)
			png_set_interlace_handling(png_ptr);
	}

	png_read_update_info(png_ptr, info_ptr);

	int depth = 0;
	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
				&color_type, NULL, NULL, NULL);
	switch (color_type) {
		case PNG_COLOR_TYPE_GRAY:       depth = 1 * bit_depth; break;
		case PNG_COLOR_TYPE_RGB:        depth = 3 * bit_depth; break;
		case PNG_COLOR_TYPE_RGB_ALPHA:  depth = 4 * bit_depth; break;
		default:                        assert(0); break;
	}

	// Read data and fill to Picture
	{
		int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
		png_bytep row_pointers[height];
		memset(row_pointers, 0, sizeof(row_pointers));
		row_pointers[0] = png_malloc(png_ptr, rowbytes * height);
		for (int row = 1; row < height; row++)
			row_pointers[row] = row_pointers[row - 1] + rowbytes;
		png_read_image(png_ptr, row_pointers);
		if (need_premultiply)
			for (int row = 0; row < height; row++)
				simg_data32_premultiply(row_pointers[row], width);
		if (unlikely(!(pictw = simg_data_to_pictw(ps, width, height, depth,
							row_pointers[0], rowbytes)))) {
			printfef("(\"%s\"): Failed to create Picture.", path);
			goto spng_read_end;
		}
	}

spng_read_end:
	if (png_ptr)
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
	if (fp)
		fclose(fp);

	return pictw;
}
Example #6
0
pictw_t *
simg_postprocess(session_t *ps, pictw_t *src, enum pict_posp_mode mode,
		int twidth, int theight, enum align alg, enum align valg,
		const XRenderColor *pc) {
	static const XRenderColor XRC_TRANS = {
		.red = 0, .green = 0, .blue = 0, .alpha = 0
	};
	if (!pc) pc = &XRC_TRANS;

	const int depth = 32;
	pictw_t *dest = NULL;
	bool transformed = false;

	if (!src) {
		if (twidth && theight) {
			if (!(dest = create_pictw(ps, twidth, theight, depth)))
				printfef("(): Failed to create Picture.");
			else
				XRenderFillRectangle(ps->dpy, PictOpSrc, dest->pict, pc, 0, 0,
						twidth, theight);
		}
		goto simg_postprocess_end;
	}

	if (!(twidth || theight)
			|| (twidth == src->width && theight == src->height))
		return src;

	// Determine composite paramaters. We have to do this before create
	// Picture because the width/height may need to be calculated.
	int width = src->width, height = src->height;
	if (!twidth) twidth = (double) theight / height * width;
	else if (!theight) theight = (double) twidth / width * height;
	double ratio_x = 1.0, ratio_y = 1.0;
	switch (mode) {
		case PICTPOSP_ORIG: break;
		case PICTPOSP_TILE: break;
		case PICTPOSP_SCALE:
		case PICTPOSP_SCALEK:
		case PICTPOSP_SCALEE:
		case PICTPOSP_SCALEEK:
							{
								if (twidth) ratio_x = (double) twidth / width;
								if (theight) ratio_y = (double) theight / height;
								if (PICTPOSP_SCALEK == mode || PICTPOSP_SCALEEK == mode)
									ratio_x = ratio_y = MIN(ratio_x, ratio_y);
								if (PICTPOSP_SCALEE == mode || PICTPOSP_SCALEEK == mode) {
									ratio_x = MAX(1.0f, ratio_x);
									ratio_y = MAX(1.0f, ratio_y);
								}
								width *= ratio_x;
								height *= ratio_y;
							}
							break;
		default:            assert(0); break;
	}

	if (!(dest = create_pictw(ps, twidth, theight, depth))) {
		printfef("(): Failed to create Picture.");
		goto simg_postprocess_end;
	}

	int x = 0, y = 0;
	int num_x = 1, num_y = 1;
	if (PICTPOSP_TILE == mode) {
	    // num_x = ceil((float) twidth / width);
	    // num_y = ceil((float) theight / height);
	    num_x = twidth / width;
	    num_y = theight / height;
	}
	switch (alg) {
		case ALIGN_LEFT:  break;
		case ALIGN_RIGHT: x = twidth - width * num_x; break;
		case ALIGN_MID:   x = (twidth - width * num_x) / 2; break;
	};
	switch (valg) {
		case ALIGN_LEFT:  break;
		case ALIGN_RIGHT: y = theight - height * num_y; break;
		case ALIGN_MID:   y = (theight - height * num_y) / 2; break;
	};
	x = MAX(x, 0);
	y = MAX(y, 0);
	int x2 = MIN(x + width * num_x, twidth),
			y2 = MIN(y + height * num_y, theight);
	/* if (pc->alpha) */ {
		if (src->depth >= 32)
			XRenderFillRectangle(ps->dpy, PictOpSrc, dest->pict, pc, 0, 0,
					twidth, theight);
		else {
			XRenderFillRectangle(ps->dpy, PictOpSrc, dest->pict, pc, 0, 0,
					twidth, y);
			XRenderFillRectangle(ps->dpy, PictOpSrc, dest->pict, pc, 0, y2,
					twidth, theight - y2);
			XRenderFillRectangle(ps->dpy, PictOpSrc, dest->pict, pc, 0, y,
					x, y2 - y);
			XRenderFillRectangle(ps->dpy, PictOpSrc, dest->pict, pc, x2, y,
					twidth - x2, y2 - y);
		}
	}
	if (src->pict) {
		if (1.0 != ratio_x || 1.0 != ratio_y) {
			XTransform transform = { .matrix = {
				{ XDoubleToFixed(1.0 / ratio_x), XDoubleToFixed(0.0), XDoubleToFixed(0.0) },
				{ XDoubleToFixed(0.0), XDoubleToFixed(1.0 / ratio_y), XDoubleToFixed(0.0) },
				{ XDoubleToFixed(0.0), XDoubleToFixed(0.0), XDoubleToFixed(1.0) },
			} };
			transformed = true;
			XRenderSetPictureTransform(ps->dpy, src->pict, &transform);
		}