News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Problem with GDI+

Started by donkey, February 10, 2010, 04:38:40 AM

Previous topic - Next topic

donkey

I've been having a bit of a problem with GDI+, I have noticed that the images when stretched using GdipDrawImageRectI are shifted up and to the left. I can't figure out why this is happening or how to correct it. The amount of shift increases as the size of the source image gets smaller, I have posted the code here as well as a small app that demonstrates the problem. All API calls return with no error.

DATA SECTION
hInstance HANDLE ?
GDIPlusToken ULONG_PTR 0
gdipsi GdiplusStartupInput <1,0,0,0>
pPic1 PTR 0
pPic2 PTR 0
ptrStaticProc PTR ?

CODE SECTION
START:
invoke GetModuleHandle, 0
mov [hInstance],eax
invoke GdiplusStartup,offset GDIPlusToken,offset gdipsi,NULL
invoke DialogBoxParam,[hInstance],1000,0,ADDR DlgProc,0
invoke GdipDisposeImage,[pPic1]
invoke GdipDisposeImage,[pPic2]
invoke GdiplusShutdown,[GDIPlusToken]
invoke ExitProcess,0

DlgProc FRAME hwnd,uMsg,wParam,lParam

cmp D[uMsg],WM_INITDIALOG
jne >.WM_CLOSE
invoke GdipLoadImageFromFile,"basebmp.bmp",offset pPic1
invoke GdipLoadImageFromFile,"basebmp2.bmp",offset pPic2
invoke GetDlgItem,[hwnd],1001
invoke SetWindowLong,eax,GWL_WNDPROC,offset StaticProc1
mov [ptrStaticProc],eax
invoke GetDlgItem,[hwnd],1002
invoke SetWindowLong,eax,GWL_WNDPROC,offset StaticProc2
jmp >.EXIT

.WM_CLOSE
cmp D[uMsg],WM_CLOSE
jne >.DEFPROC
INVOKE EndDialog,[hwnd],0

.DEFPROC
mov eax,FALSE
ret

.EXIT

mov eax, TRUE
ret
ENDF

StaticProc1 FRAME hwnd,uMsg,wParam,lParam
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
LOCAL pGraphics:%PTR

.WM_PAINT
cmp D[uMsg],WM_PAINT
jne >>.DEFPROC
invoke BeginPaint,[hwnd],offset ps
invoke GetClientRect,[hwnd],offset rect
invoke GdipCreateFromHDC,[ps.hdc], offset pGraphics
invoke GdipSetInterpolationMode,[pGraphics],InterpolationModeHighQualityBicubic
invoke GdipDrawImageRectI,[pGraphics],[pPic1],0,0,[rect.right], [rect.bottom]
invoke GdipDeleteGraphics, [pGraphics]
invoke EndPaint,[hwnd],offset ps

.DEFPROC
invoke CallWindowProc,[ptrStaticProc],[hwnd],[uMsg],[wParam],[lParam]
RET
ENDF

StaticProc2 FRAME hwnd,uMsg,wParam,lParam
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
LOCAL pGraphics:%PTR

.WM_PAINT
cmp D[uMsg],WM_PAINT
jne >>.DEFPROC
invoke BeginPaint,[hwnd],offset ps
invoke GetClientRect,[hwnd],offset rect
invoke GdipCreateFromHDC,[ps.hdc], offset pGraphics
invoke GdipSetInterpolationMode,[pGraphics],InterpolationModeHighQualityBicubic
invoke GdipDrawImageRectI,[pGraphics],[pPic2],0,0,[rect.right], [rect.bottom]
invoke GdipDeleteGraphics, [pGraphics]
invoke EndPaint,[hwnd],offset ps

.DEFPROC
invoke CallWindowProc,[ptrStaticProc],[hwnd],[uMsg],[wParam],[lParam]
RET
ENDF


Note that GdipSetInterpolationMode is being used to suppress anti-aliasing and can be removed without changing the result. The sample application has 2 static controls each 192x192 pixels, the top one displays a 16x16 white square and the bottom one a 32x32 white square, both are supposed to be stretched to the size of the control.

Any insight would be greatly appreciated.

Edgar
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

BlackVortex

Maybe the resizing function performs a rounding. And that's why the error is bigger for lesser values.

Just a guess to get things rolling, I have no real experience with GDI stuff  :red

donkey

Quote from: BlackVortex on February 10, 2010, 05:33:06 AM
Maybe the resizing function performs a rounding. And that's why the error is bigger for lesser values.

Just a guess to get things rolling, I have no real experience with GDI stuff  :red

Hi BlackVortex,

I was worried that that might be the problem, that's why when I was testing it I used a stretch with integer multiples, no rounding needed (x12) and (x6). Also at 96 dpi the size of the statics is an even multiple as well. I made an effort to forestall any rounding problems. But I agree that that's what it looks like, can't figure out why though, I mean StretchBlt handled it just fine in the old GDI.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

A close examination of the output shows that the amount of displacement is always 1/2 of a stretched pixel. So if a pixel is expanded to 24x24 the displacement will be 12 left and 12 top. This seems like it may be the way gdip handles the zoom, it begins the zoom at the center of the expanded pixel in the top left and continues toward the bottom right. The displacement is consistent across all my tests and though I haven't looked at all the examples here, the ones I did see have it as well. I think the solution is to create a memory DC slightly larger than the expanded image and use GdipTranslateWorldTransform to shift the image within it, then copy it into the DC using another translate transform to shift it back to the original position. I'll keep you posted when I get some tests set up and results.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

Well, after a lot of experiments I have found the solution, the pixel offset mode has to be set (my guess above seems to be right):

invoke GdipSetPixelOffsetMode,[pGraphics],PixelOffsetModeHighQuality

This will take care of the shift in the image to the left and top. In the WM_PAINT handler:

.WM_PAINT
cmp D[uMsg],WM_PAINT
jne >>.WM_LBUTTONDOWN
invoke BeginPaint,[hwnd],offset ps
invoke GetClientRect,[hwnd],offset rect
invoke GdipCreateFromHDC,[ps.hdc], offset pGraphics
invoke GdipSetPixelOffsetMode,[pGraphics],PixelOffsetModeHighQuality
invoke GdipSetInterpolationMode,[pGraphics],InterpolationModeHighQualityBicubic
invoke GdipDrawImageRectI,[pGraphics],[pPic],0,0,[rect.right], [rect.bottom]
invoke GdipDeleteGraphics, [pGraphics]
invoke EndPaint,[hwnd],offset ps
xor eax,eax
ret


with this I get a faithful reproduction of the zoomed image without any anti-aliasing effect. So for all of those who have written a GDI+ viewer you might think about adding the pixel offset correction.

Edgar
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

jj2007

Have you tried GdipDrawImageRect (no I at the end)? The float version *should* be immune against rounding problems...

donkey

Quote from: jj2007 on February 11, 2010, 07:11:50 AM
Have you tried GdipDrawImageRect (no I at the end)? The float version *should* be immune against rounding problems...

Hi JJ2007,

No, though it will not make a difference since this is not a rounding problem but a problem in the way the offset of the pixel is determined. As i said, I have the solution in GdipSetPixelOffsetMode and it makes sense when you examine how GDI+ expands images.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable