Groups | Blog | Home
all groups > dotnet drawing api > october 2007 >

dotnet drawing api : StretchDIBits? Vista? Nvidia?



Ivan Brugiolo [MSFT]
10/10/2007 7:54:21 PM
Can you clarify:
The driver model for the two cards, and if Desktop Composition is enabled/
The color-depth of the device.
If you are using device-compatible DCs, or not.

My theory:
StrechBits is forced to do a 24-to-32 bpp conversion, and, it goes through
a palettized translation in one case, and,
it's done natively by the *non* WDDM driver in the other case.

In Vista, 24bpp was remove from the available
video-modes for WDDM drivers for issues like this.

--

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


[quoted text, click to view]

Norman Diamond
10/11/2007 12:00:00 AM
Mr. Brugiolo, thank you for your interest in this problem.

The older card is on an XP-only machine, so I think that Desktop Composition
doesn't exist. If I'm mistaken, please say how to check. Anyway, that's
where the program is working.

On the Vista machine with the newer chip, Aero is working, and I think that
means that Desktop Composition is working. Again if I'm mistaken, please
say how to check whether Desktop Composition is working.

In all cases the desktops are running at 32 bpp.

For the destination DCs, I call Graphics.GetHdc on two kinds of Graphics
objects. One comes from a PictureBox (displayed on the screen as a child
window, right?). The other comes from a Bitmap object (memory only). I
have not tried to investigate if those DCs are device-compatible; I just
assumed that the first one would be and the second one would not. Again in
XP with the old card there was no problem with either, and in Vista with the
new chip there are problems with both.

The bits arrays themselves are 24bpp. I assumed that StretchDIBits does
24-to-32 bpp conversion for the target PictureBox. For the target Bitmap, I
create it with System.Drawing.Imaging.PixelFormat.Format24bppRgb so I
assumed that StretchDIBits would not have to convert that. Again in XP with
the old card there was no problem with either, and in Vista with the new
chip there are problems with both.

I can guess how unwanted palettization could cause problems, but can't
imagine how it could choose its victims in this particular way.

I can't imagine how removal of 24bpp video modes would help this issue. A
JPEG file on disk is still 24bpp and still needs conversion to 32bpp for
display.

Now, the situation is getting weirder. The Vista machine with the new chip
can also boot XP, and belatedly I tried it. The same problem arises there.
Now I'm guessing that the problem is avoided with XP on the old card because
acceleration in the old card did something that was too friendly ^_^ In a
while I will try booting the old machine to XP in safe mode and see if that
avoids the friendly behaviour. But I'm still really puzzled, because I
think StretchDIBits should always do this friendly vertical flipping and
avoid converting to grayscale, no matter what hardware or degree of
acceleration.


[quoted text, click to view]
Norman Diamond
10/11/2007 12:00:00 AM
I've just tried a Windows 2000 machine with an antique Nvidia card and the
result was the same as the Vista machine.

Maybe there's something strange about the moderately-old card (Nvidia
GeForce FX 5200) or there's some effect from one of the preinstalled Dell
applications that made StretchDIBits work on one machine. Anyway it seems
pretty clear now that StretchDIBits isn't likely to work on most machines
the way it does on this one.

Now I have to go back and find some more reliable way to try to get
StretchDIBits working. There is some other image format that it works with
correctly; I'll have to inspect the BITMAPINFO of that format.
Norman Diamond
10/11/2007 12:00:00 AM
I'm starting to get somewhere by using bottom-up DIBs without any flipping,
though no longer sure if that was even necessary or not. The former
appearance of having been upside-down (i.e. not flipped when it was supposed
to be flipped) might have been a coincidence, as an effect of violating the
following constraint.

Hidden at the bottom of an oddly chosen MSDN page is this gem:
http://msdn2.microsoft.com/en-us/library/ms532337.aspx
[quoted text, click to view]

Is there any reason why that information isn't in either or both of the
following MSDN pages?
http://msdn2.microsoft.com/en-us/library/ms532354.aspx
http://msdn2.microsoft.com/en-us/library/ms532317.aspx
Don't these two pages describe the general format of DIBs instead of just
the headers? Wouldn't it be more useful to describe the bits here? After
all, when calling StretchDIBits, the lpBits parameter is separate from the
lpBitsInfo parameter.

Now, it seems that even when hardware acceleration is completely disabled,
one variety of Nvidia hardware does a StretchDIBits without enforcing that
DIB row alignment (padding) requirement, one variety does enforce it, and I
need to test some others. Except that this really doesn't make sense. On
the one variety that doesn't enforce it, how does it detect if padding is
present or not, how could it make the appearance come out right both when
padding is present and when padding is absent?
Chris Becke
10/11/2007 12:00:00 AM
I think you are trying too hard to deal with the bottom up-ness of the DIB.

While the DIB is bottom up - and you need to know how to deal with that when
directly dealing with the DIB bits your self, StretchDIBits is aware of the
bottom up-ness of the DIB.

Which means, when you pass 0,0 and a positive height to StretchDIBits - it
internally does the bottom up translations to get to the correct part of the
DIB.

So, pass top-left co-ordinates to StretchDIBits, dont fiddle with the
height, let GDI take care of the translations internally.


[quoted text, click to view]

Norman Diamond
10/11/2007 12:00:00 AM
By now I've made all the DIBs bottom up (in the real image as well as in the
DIB format), stopped asking StretchDIBits to flip it vertically, and added
padding as mentioned in a recent post.

However, I cannot pass all top-left coordinates to StretchDIBits. For the
destination DC the top-left is correct (now). For the source bits and info,
I have to specify the bottom-left (now). If I specify the top-left for both
then it still doesn't work.


[quoted text, click to view]
Norman Diamond
10/11/2007 12:00:00 AM
A C++ DLL has bits for a DIB and a BITMAPINFO structure. biBitCount is 24
and biHeight is positive, treating the DIB as bottom-up[*]. On 32-bit
Windows XP and Vista systems, a C# program has Int32 variables that are
actually pointers[**] and there is no problem with those. The destination
DC comes from a Graphics of either a PictureBox or an in-memory Bitmap. I
platform invoke to StretchDIBits.

Since the DIB itself is upside down from what we want[*], for the source
coordinates I specify the lower left corner instead of upper left, and for
the destination height I specify a negative number. This gets the image
transported to the destination DC.

In XP, with an Nvidia GeForce FX 5200 operating in 32 bits, the result is
perfect. The PictureBox shows the image right-side up with correct colours,
and the in-memory Bitmap gets a capture right-side up with correct colours.
I thought my program was working.

In Vista, with an Nvidia GeForce Go 7600 operating in 32 bits, the result is
awful. The PictureBox shows the image upside-down in grayscale, and the
in-memory Bitmap gets a capture upside-down in grayscale.

Does anyone know what's broken? The StretchDIBits API in Vista? Nvidia's
driver for Vista? The different Nvidia card? Graphics.GetHdc in Vista?

I didn't even recompile the program in Vista (yet). I copied the EXE and
DLLs, and somehow got manifests working for registration-free COM, working
in both XP and Vista.


[* I will experiment with changing that but it's not supposed to be a
problem.]

[** Someday I might post a question somewhere else asking why IntPtr works
in 32-bits but doesn't work in 64-bits. Anyway that's not today's
question.]
Ivan Brugiolo [MSFT]
10/11/2007 3:26:26 AM
DIB has always had the requirement
of a DWORD alignment on a per scanline basis.
Last time I debugged an XPDM nVidia driver, I remeber that
internally the frame-buffer and device compatible bitmaps
had alignment requirement greater than DWORD, for
a reason that looked to me some segmentaton requirement of their
video-memory architecture.
Still, I would like to see a clear matrix of what works and waht does not
work,
follwoing the table I was asking at the beginning:
Operative System, Driver model, dekstop Compostion on/off,
device-compatible bitmaps vs DIB, etc, etc.

To make the problem more concrete:
is the scenario using win32k!EngStretchBlt or <display_driver>!DrvStrectBlt
?
For the first usage, we can come out with an explantion.
for the second usage, there is not much that can be done.

--

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


[quoted text, click to view]

Ivan Brugiolo [MSFT]
10/11/2007 3:28:08 AM
One othe trick to flip top-down / bottom-up
is to pass a negative coordinate for the `y` dimension
when CreateDIBSection is being used.
this does not remove the DWORD alignment requirements.

--

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


[quoted text, click to view]

Chris Becke
10/11/2007 2:14:37 PM
Without code samples, it is difficult to determine what you are doing. I
also can;t post anything other than raw C example code so this post is a tad
OT for the .dotnet group :P

Anway - assuming a DIB represented by BITMAPINFO* pbmi and LPVOID pvBits.

The BITMAPINFOHEADER is filled in with all positive values for width and
height and pvBits points to the bottom-up buffer containing the pixels in
the relevent format.

Then, to draw any part of this DIB onto a DC, the following code *should*
suffice :-
int cx = pbmi->bmiHeader.biWidth;
int cy = abs(pbmi->bmiHeader.biHeight);
StretchDIBits(hdc,x,y,cx,cy,0,0,cx,cy,pbmi,pvBits,DIB_RGB_COLORS,SRCCOPY);

Where x,y refers to the DIB to be blitted, and cx and cy are the width and
(non negative) height of the DIB. (I put the 'abs' is there just-in-case
someone feeds a top-down DIB into the system.)

Something is really funny if you need to do anything more complex than that.

[quoted text, click to view]

Norman Diamond
10/12/2007 12:00:00 AM
[quoted text, click to view]

Fine, but why does MSDN hide that fact at the bottom of an
irrelevant-looking page? Why doesn't MSDN document it in one or two pages
which look a lot more relevant? I gave their URLs and explained why the
other two look more relevant.

[quoted text, click to view]

I suppose that could have some impact on StretchDIBits, but I don't quite
see what the application can do about it. The main EXE uses .Net, has a
PictureBox, and calls Graphics.GetHdc. I think that PictureBox has a
default ErrorImage, but anyway the application doesn't select a bitmap into
it, the application only does a StretchDIBits to it.

Sorry I still don't know how to check if Desktop Composition is on or not.
If it means the same as having the Aero interface working (translucent
borders and blurred partially obscured lower windows) then the answer is
that it is on in Vista (business) but not in XP or W2K.

[quoted text, click to view]

Oh, my windbag experiments were 3 years ago and I'll have to figure out how
to use it again. If I have time for it, I'll let you know in a few days or
weeks.

Meanwhile true bottom-up DIBs without flipping but with padding are working
on three machines at the moment, so the rest of that application has
priority at the moment.


[quoted text, click to view]
Norman Diamond
10/12/2007 12:00:00 AM
Since the .Net code is P/Invoking to StretchDIBits, surely a raw C example
of StretchDIBits is on-topic. The only thing missing is the Assembly ^_^

With a truly bottom-up DIB (no flipping), using your code
[quoted text, click to view]
I would define the parameters a bit differently.

(x,y) is the upper-left corner of the destination rectangle in the
destination DC, not the DIB.

(0,0) is the lower-left corner of the source rectangle in the DIB, counting
the first 0 from the left and counting the second zero from the bottom. If
you're really blitting the entire DIB then (0,0) is correct. If you're
blitting a rectangular portion of the DIB then you have to notice that the
unnamed y coordinate here starts at 0 for the bottom row and counts up to
cy-1 (using the second cy) for the top row.

If you're shrinking or stretching then of course the second cx,cy don't
match the first cx,cy.


[quoted text, click to view]
Chris Becke
10/12/2007 12:00:00 AM
Damn these DIBs are tricky beasts. You are right of course: 0,0 is the
bottom left corner of the DIB - just to double the whole upside-downness
insanity of it all.

I blame those nutters at IBM who thought this all would be a good idea.

[quoted text, click to view]

Ivan Brugiolo [MSFT]
10/12/2007 12:04:28 AM
Vista can have any of the two supported driver model: XPDM and WDDM.
With a capable WDDM driver, and if it is enabled or allowed by policy,
it can have desktop composition enabled.
Desktop Composition can have opaque or transparent glass, so
the `aero looking` perception is not good enough.
Where is an API dwampi!IsCompositionActive that can solve that problem.
The actual implementation of StrecthBlt can be different depending on the
cases.

DIBs are always the easy case, while device compatible bitmaps, are,
as their name suggests, depending upon the device.

--

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


[quoted text, click to view]

Ivan Brugiolo [MSFT]
10/14/2007 8:42:06 PM
The fact that desktop composition is enabled does not mean it is active.
There are well known cases where this can and does happen.
The API suggested gives the runtime information.

This does not help with your scan-line DWORD alignment issue,
but, it should clarify one of the possible circumstances where
some behavior may be different

--

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


[quoted text, click to view]
Norman Diamond
10/15/2007 12:00:00 AM
In System properties in Vista x86 and Vista x64, I found Desktop Composition
and it is on (checkmark present). Of course in XP and 2000 there is no such
thing.

I think that the failure to pad each scan line to a 4-byte boundary was the
reason for problems on all except one machine. I still can't figure out why
it wasn't a problem on the machine where I did the programming.

I still wish, as suggested before, that the requirement for 4-byte alignment
(padding) of each scan line would be stated in two MSDN pages that look a
lot more relevant to the overall structure of DIBs, instead of buried in the
MSDN page about Bitmap Header Types.

[quoted text, click to view]
Chris Becke
10/15/2007 2:47:58 PM
A lack of DWORD alignment is going to cause horizontal shearing in the
image - and at anything other than 8bpp - cause each line to be miscolored
differently.

Horizontal shear is quite noticable, and the discouloring would tend to not
produce a nice neat greyscale effect. Given that most programmers tend to
"randomly pick" numbers that are DWORD aligned anyway, im not buying DWORD
alignment as much more than a co-incidental bug :- something that needs to
be fixed yes, but not actually implicated in this scenario


[quoted text, click to view]
Norman Diamond
10/16/2007 12:00:00 AM
In this case horizontal shear in the DIB made the target rectangle in the
PictureBox appear upside down, but that was a coincidence, because of the
shapes of major features in the image. It wasn't really upside down, it was
shear. The resolution was 24bpp in the DIB and 32bpp on the screen. The
colour came out looking like grayscale, which seems to be a coincidence.
This programmer "randomly picked" a number that was the number of bytes in
each scan line (3 * width because it was 24bpp), so it wasn't DWORD aligned.

This programmer later found a relevant statement buried at the bottom of an
MSDN page which seems to be irrelevant, and still did not find relevant
statements in MSDN pages which seem to be relevant. Anyway, padding each
scan line to force DWORD alignment, instead of randomly actually matching
the actual source image, seems to have fixed both apparent problems. So it
looks like the absence of padding was implicated in this scenario.


[quoted text, click to view]
Grzegorz_Wróbel
10/16/2007 12:00:00 AM
[quoted text, click to view]

It will cause some lines to be miscolored only in case of 24bpp bitmap;
at anything other than 24bpp - no.

--
Grzegorz Wróbel
http://www.4neurons.com/
AddThis Social Bookmark Button