#include #include using namespace Gdiplus; void DrawMarks(Graphics* graphics, Pen* greenPen, Pen* redPen, const Rect& r) { graphics->DrawRectangle(redPen, r); graphics->DrawLine(greenPen, r.X+r.Width-1, r.Y-5, r.X+r.Width-1, r.Y+5); // end markers graphics->DrawLine(greenPen, r.X-5, r.Y+r.Height-1, r.X+5, r.Y+r.Height-1); } extern "C" void SimpleImageTest(HWND hWnd) { int x, y; Graphics* graphics = new Graphics(hWnd); graphics->Clear(Color(255, 255, 255)); // white background Bitmap image(16, 16, PixelFormat24bppRGB); image.SetResolution(graphics->GetDpiX(), graphics->GetDpiX()); /* black pixel border */ for (y = 0; y < 16; y++) image.SetPixel(0, y, Color(0, 0, 0)); for (y = 0; y < 16; y++) image.SetPixel(15, y, Color(0, 0, 0)); for (x = 1; x < 15; x++) image.SetPixel(x, 0, Color(0, 0, 0)); for (x = 1; x < 15; x++) image.SetPixel(x, 15, Color(0, 0, 0)); /* light yellow contents */ for (y = 1; y < 15; y++) for (x = 1; x < 15; x++) image.SetPixel(x, y, Color(192, 192, 0)); Pen redPen(Color(255, 0, 0), 1); redPen.SetDashStyle(DashStyleDash); Pen greenPen(Color(0, 255, 0), 1); greenPen.SetDashStyle(DashStyleDash); // Standard Usage // NO zoom { Rect actualRect(10, 10, 16, 16); graphics->DrawImage(&image, 10, 10); DrawMarks(graphics, &greenPen, &redPen, actualRect); } // zoom using Bilinear Interpolation { Rect zoomRect(50, 10, 160, 160); graphics->SetInterpolationMode(InterpolationModeBilinear); graphics->DrawImage(&image, zoomRect); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } // zoom using Nearest Neighborhood { Rect zoomRect(250, 10, 160, 160); graphics->SetInterpolationMode(InterpolationModeNearestNeighbor); graphics->DrawImage(&image, zoomRect); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } // Experiment using a source image size, greater than actual // The image is resized down!!! // NO zoom { Rect actualRect(10, 200, 16, 16); graphics->DrawImage(&image, actualRect); DrawMarks(graphics, &greenPen, &redPen, actualRect); } // zoom using Bilinear Interpolation { Rect zoomRect(50, 200, 160, 160); graphics->SetInterpolationMode(InterpolationModeBilinear); graphics->DrawImage(&image, zoomRect, 0, 0, 20, 20, UnitPixel, NULL, NULL); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } // zoom using Nearest Neighborhood { Rect zoomRect(250, 200, 160, 160); graphics->SetInterpolationMode(InterpolationModeNearestNeighbor); graphics->DrawImage(&image, zoomRect, 0, 0, 20, 20, UnitPixel, NULL, NULL); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } // Use the Experiment results to try a solution // Using a source image size, smaller than actual // NO zoom { Rect actualRect(10, 400, 16, 16); graphics->SetInterpolationMode(InterpolationModeNearestNeighbor); graphics->DrawImage(&image, actualRect, 0, 0, 16-1, 16-1, UnitPixel, NULL, NULL); DrawMarks(graphics, &greenPen, &redPen, actualRect); } // zoom using Bilinear Interpolation { Rect zoomRect(50, 400, 160, 160); graphics->SetInterpolationMode(InterpolationModeBilinear); graphics->DrawImage(&image, zoomRect, 0, 0, 16-1, 16-1, UnitPixel, NULL, NULL); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } // zoom using Nearest Neighborhood { Rect zoomRect(250, 400, 160, 160); graphics->SetInterpolationMode(InterpolationModeNearestNeighbor); graphics->DrawImage(&image, zoomRect, 0, 0, 16-1, 16-1, UnitPixel, NULL, NULL); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } // First Solution { Rect zoomRect(600, 10, 160, 160); graphics->SetInterpolationMode(InterpolationModeBilinear); int errorBand = 10; // zoomRect.Width / actualRect.Width; Rect zoomRectClip(600-1, 10-1, 160+2, 160+2); Rect zoomRectError(600, 10, 160 + errorBand, 160 + errorBand); graphics->SetClip(zoomRectClip); graphics->DrawImage(&image, zoomRectError); graphics->ResetClip(); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } // Solution suggested by Michael Phillips, Jr. { Rect zoomRect(600, 200, 160, 160); graphics->SetInterpolationMode(InterpolationModeNearestNeighbor); graphics->SetPixelOffsetMode(PixelOffsetModeHalf); // pixel center is (.5,.5) instead of (0, 0) graphics->SetClip(zoomRect); // clip out the badly interpolated pixels zoomRect.Width += 4; // force gdi+ to scale to a slightly wider area, the dropped pixels will be clipped out graphics->DrawImage(&image, zoomRect, 0, 0, 16, 16, UnitPixel, NULL, NULL, NULL); graphics->ResetClip(); zoomRect.Width -= 4; graphics->SetPixelOffsetMode(PixelOffsetModeNone); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } // Final Solution // Use PixelOffsetModeHalf and [pixel center is (.5,.5) instead of (0, 0)] // WrapModeTileFlipXY (suggested by Vlad Grachov) ImageAttributes imAtt; imAtt.SetWrapMode(WrapModeTileFlipXY); // set wrapping mode to get rid of gray lines at sides // NO zoom { Rect actualRect(10, 400, 16, 16); graphics->DrawImage(&image, 10, 400); DrawMarks(graphics, &greenPen, &redPen, actualRect); } // zoom using Bilinear Interpolation { Rect zoomRect(50, 400, 160, 160); graphics->SetInterpolationMode(InterpolationModeBilinear); graphics->SetPixelOffsetMode(PixelOffsetModeHalf); graphics->DrawImage(&image, zoomRect, 0, 0, image.GetWidth(), image.GetHeight(), UnitPixel, &imAtt, NULL, NULL); graphics->SetPixelOffsetMode(PixelOffsetModeNone); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } // zoom using Nearest Neighborhood { Rect zoomRect(250, 400, 160, 160); graphics->SetInterpolationMode(InterpolationModeNearestNeighbor); graphics->SetPixelOffsetMode(PixelOffsetModeHalf); graphics->DrawImage(&image, zoomRect, 0, 0, image.GetWidth(), image.GetHeight(), UnitPixel, &imAtt, NULL, NULL); graphics->SetPixelOffsetMode(PixelOffsetModeNone); DrawMarks(graphics, &greenPen, &redPen, zoomRect); } delete graphics; }