Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Rotate a pixmap

347 views
Skip to first unread message

jubi

unread,
Dec 12, 2007, 3:56:25 AM12/12/07
to
With a view to rotate the whole content of a TK Canvas
(which is a X11 Pixmap), i am searching for a fast
algorithm to rotate a X11 Pixmap. I have the idea
to rotate each pixel of the source pixmap and
put it in a destination pixmap which then can be displayed
with copyarea on the canvas. So I don`t know if
i am on the right pass and i am searching for ideas
and sample code.

Thank you for help.

Kalle Olavi Niemitalo

unread,
Dec 12, 2007, 2:59:06 PM12/12/07
to
jubi <froeh...@rheinmetall-de.com> writes:

The RENDER extension includes the SetPictureTransform request,
with which you could get arbitrary rotation pretty easily, I
think. And that can apparently be combined with filtering.

If the X server does not support that extension, then you can
instead copy the pixmap into a client-side XImage and rotate
there. If the pixmap is large, doing the rotation in tiles may
help use the processor's data cache more effectively.

Certainly you should not fetch pixels from the server one at a
time.

Edward Rosten

unread,
Dec 12, 2007, 7:31:08 PM12/12/07
to


Without using an extension of the X server, you need to get an XImage
from a pixmap (using XGetImage), rotate it and put it back using
XPutImage.

To rotate the image by an angle theta, you need to be able to access
pixels. Do this using XGetPixel and XPutPixel


For each pixel (x,y) in the destination:
Move the origin to the center, (x0, y0) = (x-dest_width/2, y-
dest_height/2)
Rotate to the source image xr = x0 * cos(theta) + y0 * sin(theta)
yr = x0 * -sin(theta) + y0 * cos(theta)

Translate to the source image origin
(xs, ys) = (xr + src_height/2, y2 + src_width/2);

Copy pixel (xs, ys) from the source to (x, y) in the destination
XPutPixel(dest, x, y, XGetPixel(source, xs, ys));


This uses nearest neighbout interpolation, so it looks rather bad. To
do interpolation, you need to know how your images are coloured and
threrefore how to extract the xolours from XGetPixel, interpolate them
and put them back together.

It's also slow.

You will probably want to write some hand-coded versions not using
XGetPixel and XPutPixel for the image types you really care about.

-Ed

--
(You can't go wrong with psycho-rats.) (http://mi.eng.cam.ac.uk/
~er258)

/d{def}def/f{/Times findfont s scalefont setfont}d/s{11}d/r{roll}d f 2/
m
{moveto}d -1 r 230 350 m 0 1 179{1 index show 88 rotate 4 mul 0
rmoveto}
for /s 12 d f pop 235 420 translate 0 0 moveto 1 2 scale show
showpage

George Peter Staplin

unread,
Dec 12, 2007, 8:27:22 PM12/12/07
to

I'd suggest using XShm pixmaps or images if you do it. XGetImage and
XPutImage can be slow, but they are the fallbacks when shared memory
isn't available.

I have some code that does rotation of an image, incidentally for Tcl,
so perhaps the extension is something you could use. I'm also working
on a NexTk that uses the megaimage extension to represent window
buffers. Full RGBA support and alpha blending are also part of NexTk.

The algorithm is fairly simple. You use a standard rotation matrix and
multiply by the x,y. You first need to calculate the rotated size of
the image.

This is the algorithm I use for the final size:
static void
instance_cmd_rotate_new_size ( struct megaimage *img, int d, int
*newwidth, int *newheight ) {
double a;

a = (3.14159265359 * d) / 180;

*newwidth = round (abs (img->width * cos (a)) + abs (img->height * sin
(a)) + 2.0);
*newheight = round (abs (img->height * cos (a)) + abs (img->width * sin
(a)) + 2.0);
}

2.0 is basically a fudge factor, because the operation isn't exact with
some angles.

Then you iterate the destination image buffer, and translate each x,y
with the rotation matrix.

Here's the code I use:
http://megapkg.googlecode.com/svn/trunk/csrc/megaimage/megaimage_rotate_fixed.c

That algorithm can be much simpler. I have optimized it quite a bit
during profiling, to reduce the amount of overall computation.

I also have an alternative that's a little slower (on a Pentium 4) that
uses double floating point:
http://megapkg.googlecode.com/svn/trunk/csrc/megaimage/megaimage_rotate_float.c

By the way the svn/trunk/csrc/ntk/x tree has code for XShm.


George

Kalle Olavi Niemitalo

unread,
Dec 16, 2007, 5:59:26 AM12/16/07
to
Kalle Olavi Niemitalo <k...@iki.fi> writes:

> The RENDER extension includes the SetPictureTransform request,
> with which you could get arbitrary rotation pretty easily, I
> think. And that can apparently be combined with filtering.

I tried to set up some sample code, but it's not working.
The following should tile the contents of window 0x100002f to
the root window, but I only get black. If I change the 1.01 to
exact 1.0, then it works all right. This is with Debian
xserver-xorg 1:7.1.0-7 and xserver-xorg-video-mga 1:1.4.4.dfsg.1-1.
(I'm not using later versions yet, because of Debian bug 430112.)
Is it a bug in the X server, or did I misunderstand something?

#! /usr/bin/tcc -run -lX11 -lXrender -lm

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>
#include <math.h>

static XFixed
float_to_fixed(float f)
{
return f * 0x10000;
}

int
main(void)
{
int mainret = EXIT_FAILURE;
Display *display = NULL;
int render_event_base;
int render_error_base;
int render_major_ver;
int render_minor_ver;
Window src_window;
Window dst_window;
Picture src_picture = None;
Picture dst_picture = None;
XRenderPictFormat *src_pictformat = NULL;
XRenderPictFormat *dst_pictformat = NULL;
XRenderPictureAttributes pictattr = {0};
XWindowAttributes src_window_attributes;
XWindowAttributes dst_window_attributes;
XTransform transform;

display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Cannot open display\n");
goto error;
}

if (!XRenderQueryExtension(display, &render_event_base, &render_error_base)) {
fprintf(stderr, "RENDER extension not found and alternative code not yet implemented\n");
goto error;
}
if (!XRenderQueryVersion(display, &render_major_ver, &render_minor_ver)) {
fprintf(stderr, "RENDER extension version is unclear\n");
goto error;
}
if (render_major_ver != 0 || render_minor_ver < 10) {
fprintf(stderr, "RENDER extension version is incompatible\n");
goto error;
}

pictattr.repeat = RepeatNormal;
src_window = 0x100002f; /* change this */
dst_window = DefaultRootWindow(display);
if (!XGetWindowAttributes(display, src_window, &src_window_attributes)) {
fprintf(stderr, "Cannot retrieve source window attributes\n");
goto error;
}
if (!XGetWindowAttributes(display, dst_window, &dst_window_attributes)) {
fprintf(stderr, "Cannot retrieve destination window attributes\n");
goto error;
}
src_pictformat = XRenderFindVisualFormat(display,
src_window_attributes.visual);
dst_pictformat = XRenderFindVisualFormat(display,
dst_window_attributes.visual);
src_picture = XRenderCreatePicture(display, src_window, src_pictformat,
CPRepeat, &pictattr);
dst_picture = XRenderCreatePicture(display, dst_window, dst_pictformat,
CPRepeat, &pictattr);

transform.matrix[0][0] = float_to_fixed(1.01);
transform.matrix[0][1] = float_to_fixed(0.0);
transform.matrix[0][2] = float_to_fixed(0.0);
transform.matrix[1][0] = float_to_fixed(0.0);
transform.matrix[1][1] = float_to_fixed(1.0);
transform.matrix[1][2] = float_to_fixed(0.0);
transform.matrix[2][0] = float_to_fixed(0.0);
transform.matrix[2][1] = float_to_fixed(0.0);
transform.matrix[2][2] = float_to_fixed(1.0);
XRenderSetPictureTransform(display, src_picture, &transform);
XRenderSetPictureFilter(display, src_picture, "fast", NULL, 0);

XRenderComposite(display, PictOpSrc, src_picture, None, dst_picture,
0, 0, 0, 0, 0, 0,
dst_window_attributes.width, dst_window_attributes.height);
XSync(display, False);

mainret = EXIT_SUCCESS;
error:
if (src_picture != None)
XRenderFreePicture(display, src_picture);
if (dst_picture != None)
XRenderFreePicture(display, dst_picture);
if (display != NULL)
XCloseDisplay(display);
return mainret;
}

Kalle Olavi Niemitalo

unread,
Dec 25, 2007, 6:20:27 AM12/25/07
to
Kalle Olavi Niemitalo <k...@iki.fi> writes:

> The following should tile the contents of window 0x100002f to
> the root window, but I only get black. If I change the 1.01 to
> exact 1.0, then it works all right. This is with Debian
> xserver-xorg 1:7.1.0-7 and xserver-xorg-video-mga 1:1.4.4.dfsg.1-1.

It doesn't work with xserver-xorg-core 2:1.4.1~git20071212-2 and
xserver-xorg-video-mga 1:1.9.100.dfsg.1-1, either. There seems
to be another bug too: when I make the destination window so
large that the source has to wrap, it wraps to the edge of the
root window, rather than to the edge of the source window.
The revised demo program follows.

#! /usr/bin/tcc -run -lX11 -lXrender -lm

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>
#include <math.h>

enum
{
WM_DELETE_WINDOW,
ATOMCOUNT
};

static char *atom_names[ATOMCOUNT] = {
"WM_DELETE_WINDOW"
};

static Window
create_dst_window(Display *display, const Atom atoms[], int argc, char **argv)
{
Screen *screen;
Window dst_window;
XWMHints wm_hints;
Atom protocols[1];
XSetWindowAttributes attributes;

screen = DefaultScreenOfDisplay(display);
attributes.event_mask = ExposureMask;
attributes.bit_gravity = ForgetGravity;
dst_window = XCreateWindow(display, RootWindowOfScreen(screen),
0, 0, 302, 202, 1,
DefaultDepthOfScreen(screen), InputOutput,
DefaultVisualOfScreen(screen),
CWEventMask | CWBitGravity,
&attributes);

wm_hints.flags = InputHint;
wm_hints.input = False;
XmbSetWMProperties(display, dst_window,
"xrender-rotate", NULL, argv, argc,
NULL, &wm_hints, NULL);
protocols[0] = atoms[WM_DELETE_WINDOW];
XSetWMProtocols(display, dst_window, protocols, 1);
return dst_window;
}

static XFixed
float_to_fixed(float f)
{
return f * 0x10000;
}

static int
demonstrate_render(Display *display, Window src_window, Window dst_window)
{
int ok = 0;


Picture src_picture = None;
Picture dst_picture = None;
XRenderPictFormat *src_pictformat = NULL;
XRenderPictFormat *dst_pictformat = NULL;
XRenderPictureAttributes pictattr = {0};
XWindowAttributes src_window_attributes;
XWindowAttributes dst_window_attributes;
XTransform transform;

pictattr.repeat = RepeatNormal;


if (!XGetWindowAttributes(display, src_window, &src_window_attributes)) {
fprintf(stderr, "Cannot retrieve source window attributes\n");
goto error;
}
if (!XGetWindowAttributes(display, dst_window, &dst_window_attributes)) {
fprintf(stderr, "Cannot retrieve destination window attributes\n");
goto error;
}
src_pictformat = XRenderFindVisualFormat(display,
src_window_attributes.visual);
dst_pictformat = XRenderFindVisualFormat(display,
dst_window_attributes.visual);
src_picture = XRenderCreatePicture(display, src_window, src_pictformat,
CPRepeat, &pictattr);
dst_picture = XRenderCreatePicture(display, dst_window, dst_pictformat,
CPRepeat, &pictattr);

transform.matrix[0][0] = float_to_fixed(1.0);


transform.matrix[0][1] = float_to_fixed(0.0);
transform.matrix[0][2] = float_to_fixed(0.0);
transform.matrix[1][0] = float_to_fixed(0.0);
transform.matrix[1][1] = float_to_fixed(1.0);
transform.matrix[1][2] = float_to_fixed(0.0);
transform.matrix[2][0] = float_to_fixed(0.0);
transform.matrix[2][1] = float_to_fixed(0.0);
transform.matrix[2][2] = float_to_fixed(1.0);
XRenderSetPictureTransform(display, src_picture, &transform);
XRenderSetPictureFilter(display, src_picture, "fast", NULL, 0);

XRenderComposite(display, PictOpSrc, src_picture, None, dst_picture,
0, 0, 0, 0, 0, 0,
dst_window_attributes.width, dst_window_attributes.height);

ok = 1;


error:
if (src_picture != None)
XRenderFreePicture(display, src_picture);
if (dst_picture != None)
XRenderFreePicture(display, dst_picture);

return ok;
}

int
main(int argc, char **argv)


{
int mainret = EXIT_FAILURE;
Display *display = NULL;

Atom atoms[ATOMCOUNT];



int render_event_base;
int render_error_base;
int render_major_ver;
int render_minor_ver;
Window src_window;

Window dst_window = None;
XEvent event;

if (argc != 2) {
fprintf(stderr, "Usage: xrender-rotate WINDOWID\n");
goto error;
}
src_window = strtol(argv[1], NULL, 0);

display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Cannot open display\n");
goto error;
}

XInternAtoms(display, atom_names, ATOMCOUNT, False, atoms);

if (!XRenderQueryExtension(display, &render_event_base, &render_error_base)) {
fprintf(stderr, "RENDER extension not found and alternative code not yet implemented\n");
goto error;
}
if (!XRenderQueryVersion(display, &render_major_ver, &render_minor_ver)) {
fprintf(stderr, "RENDER extension version is unclear\n");
goto error;
}
if (render_major_ver != 0 || render_minor_ver < 10) {
fprintf(stderr, "RENDER extension version is incompatible\n");
goto error;
}

dst_window = create_dst_window(display, atoms, argc, argv);
XMapWindow(display, dst_window);

for (;;) {
XNextEvent(display, &event);
switch (event.type) {
case Expose:
if (event.xexpose.count == 0) {
if (!demonstrate_render(display, src_window, dst_window))
goto error;
}
break;
case ClientMessage:
if (event.xclient.data.l[0] == atoms[WM_DELETE_WINDOW])
goto finish;
}
}

finish:
mainret = EXIT_SUCCESS;
error:
if (dst_window != None)
XDestroyWindow(display, dst_window);

0 new messages