That can be done with transforms (like OpenGL does for example) to your viewport coordinates. It's just 2D geometry math.
Zoom is essentially a division. A 200x200 viewport with 2x zoom is actually 100x100 size in your texture (which will be expanded to fill the 200x200 viewport, effectively zooming.) After that, you just have to apply a translation to offset your zoomed area, which is a simple sum.
If you use the same zoom for both X and Y coordinates, you'll keep the aspect ratio the same. Here's some pseudocode for zooming into a full-screen texture (hence the windowX
.)
backgroundX = windowX/zoom + translationX;
backgroundY = windowY/zoom + translationY;
Bear in mind you need some kind of interpolation (graphic cards can do it for you) because 1 pixel without zoom will be split into two half-pixels with 2x zoom (1px/2 = 0.5px size each fragment.) If you use nearest neighbor for interpolation, the zoomed image will be pixelated (which may be the effect you intend.)
I always implemented this myself on shaders. Here is a section of a shader of mine that zooms a texture applied to a full-screen quad, in case it helps:
uniform ivec2 resolution; // board size (for normalization)
uniform ivec3 camera; // xy = offset, z = zoom
vec2 p = (gl_FragCoord.xy/resolution - vec2(0.5))/camera.z + camera.xy/(float)resolution;
Now p
holds my texture coordinate (in [0..1] range, that's why I need the texture resolution) for the fragment at gl_FragCoord
in screen. If you're not drawing a full-screen quad, you have to replace gl_FragCoord
with your texture coordinate (passed as a uniform or with glTexCoord
in fixed-pipeline OpenGL.)
I substract 0.5 from the fragment coordinate to center the zoom direction on screen (otherwise you would zoom into the bottom-left corner.) You don't need this, since you could also add resolution/2
to your camera offset in your application, but I prefer using the center of the screen as the origin (for convenience.)