Search code examples
c#imagecanvaswindows-runtimepixel

How to get a ScrollViewer to zoom in to see individual pixels?


I'm making an app for Windows 8.1 where it is important to be able to zoom in and examine images in detail. If I just open up the bitmap and zoom in it looks like.

Can see the pixels

However when I load the image into my app and use the ScrollViewer to zoom in I get.

Pixels are fuzzy

As it appears to be trying to interpolate pixel values for some sort of anti-aliasing.

How can I get it so that when I zoom in it shows (as best it can) the exact pixels of the image? In particular I'm using the image as the background to a canvas which is contained in a scroll viewer.


I've looked around on here and MSDN and found a pair of related questions, but as yet they don't seem to have solved my exact problem.


Solution

  • There is no easy way to go about this, your best option is to use DirectX to render the image much larger so that you can mitigate the effect of WinRT automatically interpolating pixel values.

    As someone explained on MSDN and based on this outstanding request I can't see any other way to accomplish this.

    Use Win2D

    Win2D is a DirectX inter-op library for WinRT. With this you can render the image at a much larger size, and then set the default zoom level for the scrollViewier to be very small. Because of this when you zoom in it will appear to be that you can see the individual pixels without any fuzzy/blurry interpolation because you will actually be seeing groups of 64 pixels or so all as one color. I couldn't find any way to actually override what kind of interpolation gets done so this seems to be the best method.

    1. Download Win2D as a NuGet package using Visual Studio, Win2D's quickstart guide does a good job explaining some of the setup
    2. Set up your canvas and the draw event and use the DrawImage function to render the image larger

      <ScrollViewer x:Name="Scroller" ZoomMode="Enabled"
            MinZoomFactor="0.1" MaxZoomFactor="20">
          <canvas:CanvasControl x:Name="canvas" Draw="canvas_Draw" CreateResources="create"/>
      </ScrollViewer>
      

    In the canvas_draw function.

    canvas.Width = original.Width * 10;
    canvas.Height = original.Height * 10;
    args.DrawingSession.DrawImage(bitmap,new Rect(0,0,original.Width*10,original.Height*10), new Rect(0,0,original.Width,original.Height), 1.0f, CanvasImageInterpolation.NearestNeighbor);
    
    1. Make sure to set your canvas to be larger as well
    2. In your code behind set the default zoom of your ScrollVieiwer to be appropriate so your image appears to be the same size.

    In the page constructor

    Scroller.ZoomToFactor (0.1f);
    

    Other Ways Which I Looked Into and Didn't Work

    • Making the canvas very large and using BitmapEncoder/BitmapDecoder with the interpolation mode set to NearestNeighbor, this introduced lots of visual artifacts even when scaled to a power of 2 size
    • Render options only appear to be usable in WPF and not WinRT

    It may also be possible to use some image manipulation library to simply make the bitmap 10x or so as large and then use that, but I ended up using Win2D instead.