使用gdi+绘制缩放位图的问题,会有一个像素的偏差,网上看到一篇解决方案,收藏之:
http://www.tecgraf.puc-rio.br/~scuri/gdiplus/drawimage_scale_problem.html
Here is the source code and screen shot of the problem described in:
http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.gdi/2007-01/msg00122.html
Source code: drawimage_scale_problem.cpp
The green lines marks the end of the image, or the last pixel of the image.
The red rectangle is drawn using the same rectangle coordinates used in the DrawImage.
For each test, 3 rectangles are drawn, one with no zoom, one with InterpolationModeBilinear, and one withInterpolationModeNearestNeighbor.
Notice that the first image with no zoom is correctly drawn.
The next two images do not fill the entire rectangle, and the resample is not equally balanced in the edges.
Draw using a larger rectangle and clip the result.
It works... But I have to change the current clipping, and it becomes more complicated if I have another clipping region selected. Notice that the clipping rectangle must be reduced by 1 pixel to be 100% correct.
What happens if the source rectangle is larger than the image?
The image is resized down!!! So I found that this is a valid operation and it affects the final result.
Just using a smaller source rectangle by 1 pixel, the image is correctly resized*. So the DrawImage parameterssrcwidth andsrcheight are in fact srcright andsrcbottom, that in Windows convention are outsize the image.
*But only when the image is zoomed. Notice that in the image with no zoom, using the same strategy is incorrectly drawn.
Michael Phillips Jr suggested another solution very similar of my first solution, also using clipping to get rid of the background.
Here is the result, but the resampling is not equally balanced. Notice that the clipping rectangle must be reduced by 1 pixel to be 100% correct.
None of these effects are described in the GDI+ documentation.
In my point of view it was a bug of the resampling algorithm (*see the After Words Bellow).
When using PixelOffsetModeHalf to center the pixel at (+0.5, +0.5) in the code, I found also some interesting results. See the screenshots of the problem just adding the PixelOffsetMode.
The size and position are correct (ignore the red rectangle), resampling is also equally balanced, but GDI+ insist in include the background in the resampling when usingInterpolationModeBilinear.
Now I think I understond what's going on. It is a GDI+ design decision and not a bug. But the lack of good documentation makes me lose several hours just to understand what's going on. Also the decision to include the background in the resampling is something weird for me, and not acceptable for our applications. I guess I will have to live with my solution using -1 in thesrcwidth andsrcheight.
Vlad Grachov sent me a solution for the inclusion of the background. If you set theWrapMode toWrapModeTileFlipXY the background is filled with the own image instead of the background. I checked and it worked. Thanks.
Here are the results:
Notice the top red line how it is filled with black instead of the white background. The solution code is:
ImageAttributes imAtt;
imAtt.SetWrapMode(WrapModeTileFlipXY);
graphics->SetInterpolationMode(InterpolationModeNearestNeighbor);
graphics->SetPixelOffsetMode(PixelOffsetModeHalf);
graphics->DrawImage(&image, zoomRect, 0, 0, image.GetWidth(), image.GetHeight(), UnitPixel, &imAtt);
But keep in mind that PixelOffsetModeHalf will affect the position of other primitives. It is a good idea to set PixelOffsetModeNone after drawing the image.
If you have any comments or other approaches to this problem please feel free to contact me.