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

Tk Visual, masks, Windows, Unix

61 views
Skip to first unread message

scotty...@gmail.com

unread,
Feb 7, 2018, 7:12:17 AM2/7/18
to
I've been working in the Tk canvas and I wanted to obtain an XImage from the Pixmap the canvas is drawing into, and then copy that to an image using the Tk_Tk_PhotoPutBlock() and Tk_PhotoPutZoomedBlock(). This all works now but I found some problems along the way.

The conversion between XImage and Tk_PhotoImageBlock is where most of the work has to be done. I wrote the conversion code with reference to Volume One: Xlib Programming Manual 1992 and some other references. Using the XImage bitmap_unit as the pixel bit unit width and bytes_per_line as the byte length of a line. I obtained the RGB masks from the Visual (Tk_Visual()).

Works well on my Linux machine and I tested it in 32 and 16 bit colour mode.

Across on the Windows machine things were a little different. I had sketched some circles and rectangles in various colours on the canvas for the Unix tests. When I tested on Windows I had both geometry problems (looked like raster tearing) and colour problems.

Examining the XImage data on Linux and Windows revealed this:

Linux:
# ximagePtr { width 10 height 10 xoffset 0 format 2 data { 0x80 0x00 0x00 0xff ... } byte_order 0 bitmap_unit 32 bitmap_bit_order 0 bitmap_pad 32 depth 24 bytes_per_line 40 bits_per_pixel 32 red_mask 0x00000000 green_mask 0x00000000 blue_mask 0x00000000 }

Windows:
# ximagePtr { width 10 height 10 xoffset 0 format 2 data { 0x80 0x00 0x00 0x00 ... } byte_order 0 bitmap_unit 8 bitmap_bit_order 0 bitmap_pad 0 depth 32 bytes_per_line 40 bits_per_pixel 32 red_mask 0x00000000 green_mask 0x00000000 blue_mask 0x00000000 }

On Linux I had a bitmap_unit of 32 (which it should be for a 32BPP display). On windows, also with a 32BPP display, I instead had 8! Further to this the Windows XImage->bitmap_pad is 0 when it should be 32. The rest of the information is correct.

To correct the geometry problem on Windows I had to ignore XImage->bitmap_unit and use XImage->bits_per_pixel. This does work on both platforms but I think the XImage should return much the same information on both platforms which means a 32 for bitmap_unit and bitmap_pad.

My second problem was colour. On Windows it looked like the RED and GREEN were swapped. This surprised me as I went to some effort to examine the colour masks, and work out the shift counts. I figured this would work universally. Not so.

I then examined the colour masks returned in the Visual and found the red and blue to be opposite on Windows and Linux:

Linux:
# visualPtr { red_mask 0x00ff0000 green_mask 0x0000ff00 blue_mask 0x000000ff }

Windows:
# visualPtr { red_mask 0x000000ff green_mask 0x0000ff00 blue_mask 0x00ff0000 }

But I pulled out a few of the bytes from the XImage data on both systems. You can see it above but I'll reproduce it here:

Linux: { 0x80 0x00 0x00 0xff ... }
Windows: { 0x80 0x00 0x00 0x00 ... }

Ignoring the alpha value on the RHS, the pixel data is exactly the same. This is the first pixel of a 10x10 image with blue on the left edge, so the 0x80 is the very first byte of blue data. Now looking at the pixel masks, the Linux blue_mask is 0x0000ff and picks up the blue properly. The Windows blue mask is 0x00ff0000 so instead it picks up the 0x80 as a red colour.

This is where my colour troubles came from. In the end I left this code the same and patched it later when I stored the bytes in the Tk_PhotoImageBlock:

#ifdef _WIN32
#define R_OFFSET 2
#define B_OFFSET 0
#else
#define R_OFFSET 0
#define B_OFFSET 2
#endif
blockPtr.pixelPtr[blockPtr.pitch * y + blockPtr.pixelSize * x + R_OFFSET] = (pixel & visualPtr->red_mask) >> rshift;
blockPtr.pixelPtr[blockPtr.pitch * y + blockPtr.pixelSize * x +1] = (pixel & visualPtr->green_mask) >> gshift;
blockPtr.pixelPtr[blockPtr.pitch * y + blockPtr.pixelSize * x + B_OFFSET] = (pixel & visualPtr->blue_mask) >> bshift;
blockPtr.pixelPtr[blockPtr.pitch * y + blockPtr.pixelSize * x +3] = 0xFF;

And because of the problem with the masks I have done what I was trying to avoid which is add an #ifdef _WIN32. But this does correct the problem and the tests are now the same on both Windows and Linux.

But why is the Visual colour masks wrong on Windows? I think they are hard coded that way:

TkWinDisplayChanged(
Display *display)
{
.........
} else if (screen->root_depth >= 24) {
screen->root_visual->class = TrueColor;
screen->root_visual->map_entries = 256;
screen->root_visual->red_mask = 0xff;
screen->root_visual->green_mask = 0xff00;
screen->root_visual->blue_mask = 0xff0000;

And then I wondered why the rest of the Tk system functions properly. Looking at the tkCanvPs.c module it appears to call XGetPixel(). On Windows ImageGetPixel() and PutPixel() just ignore the masks :

ImageGetPixel(
XImage *image,
int x, int y)
{
......
switch (image->bits_per_pixel) {
case 32:
case 24:
pixel = RGB(srcPtr[2], srcPtr[1], srcPtr[0]);
break;


Perhaps the Visual masks in TkWinDisplayChanged() should be changed?

Francois Vogel

unread,
Feb 7, 2018, 5:09:00 PM2/7/18
to
Le 07/02/2018 à 13:12, scotty...@gmail.com a écrit :

> But why is the Visual colour masks wrong on Windows? I think they are hard coded that way:
>
> TkWinDisplayChanged(
> Display *display)
> {
> .........
> } else if (screen->root_depth >= 24) {
> screen->root_visual->class = TrueColor;
> screen->root_visual->map_entries = 256;
> screen->root_visual->red_mask = 0xff;
> screen->root_visual->green_mask = 0xff00;
> screen->root_visual->blue_mask = 0xff0000;

This code was added through:

https://core.tcl.tk/tk/info/0eb7a8a6ca8d4336

in order to fix:

https://core.tcl.tk/tk/tktview?name=223689ffff

The patch fixing the above bug has been committed and the ticket got
closed immediately.

Then Brian Griffin provided the following interesting comment in the
ticket, and I think it was never adressed:

<quote>
I tried this out and it appears to work except in two cases:
photo images and my custom widgets. Is there something
special a custom C widget needs to do to get the colors
straight? The C code is using the Xlib emulation layer + Tk
drawing routines.

Pre-existing images appear blank, at least the photo images
do; bitmaps seem to be fine. Do these need to be handled as
a special case?

Test environment: using remote display, host is winxp,
remote is win2k. Host depth 32, remote depth 16.

What's the reason for #if 0 in tkWinWm.c:InvalidateSubTreeDepth?
</quote>


> Perhaps the Visual masks in TkWinDisplayChanged() should be changed?

Yes, perhaps it would be a good time to address Brian's comment...

Any other opinions?

Regards,
Francois


0 new messages