When printing a bitmap from a Silverlight 4 application, sometimes the result can be a little bit blurry.
For many applications this is acceptable, but in the case of this StackOverflow question,
the blurry result means that the QR code being printed can no longer be read.
It's easy to understand the cause of the bluriness. When Silverlight 4 prints, it takes a UIElement and rasterizes it to
a bitmap at a resolution suitable for printing (typically 600 DPI). Since screen resolution is around 96 DPI, the Silverlight
printing routine will "upscale" bitmaps in the UIElement. By default, this upscaling operation uses linear resampling, which
results in blurred edges (especially between black and white pixels).
In WPF, it's possible to override the type of scaling done so that the blurred edges don't occur. The attached property RenderOptions.BitmapScalingMode
exists for just this purpose. Unfortunately, that is not available in Silverlight. There is a workaround, however -- you can do the scaling yourself, and apply a ScaleTransform
to the image that exactly offsets your scaling. When the PrintDocument goes to "upscale" to 600 DPI, it will essentially
just be removing that ScaleTransform, resulting in your original image.
To demonstrate, we can start with a checkerboard image that's generated with a WriteableBitmap:
int width = 40;
int height = 40;
double checkerSize = 4;
int stride = width;
WriteableBitmap bitmap = new WriteableBitmap(width, height);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (Math.Floor(x / checkerSize) % 2 ==
Math.Floor(y / checkerSize) % 2)
bitmap.Pixels[x + stride * y] = 0xFF << 24; // Black
else
bitmap.Pixels[x + stride * y] = 0xFF << 24 |
0xFF << 16 |
0xFF << 8 |
0xFF; // White
}
}
Image image = new Image() { Stretch = Stretch.None };
image.Source = bitmap;
PrintDocument printDocument = new PrintDocument();
printDocument.PrintPage += (s, args) =>
{
args.PageVisual = image;
};
printDocument.Print("Checkerboard");
On screen, here is what the bitmap looks like:

Here's what it looks like when printed:

You can see that the edges of each square are blurred. To get rid of the blur, we first scale up our image to 600 DPI (from 96 DPI), then apply a ScaleTransform that exactly offsets the scaling:
int width = 40;
int height = 40;
double checkerSize = 4;
double scale = 600.0 / 96.0; // scale from 96 DPI to 600 DPI
width = (int)(width * scale);
height = (int)(height * scale);
checkerSize *= scale;
int stride = width;
// Same code to create the checkerboard...
Image image = new Image() { Stretch = Stretch.None };
image.Source = bitmap;
image.RenderTransform = new ScaleTransform
{
ScaleX = 96.0 / 600.0,
ScaleY = 96.0 / 600.0
};
This time when we print, the result has sharp edges:

