An image says a thousand words, so what about two? I have this map art:

In order to actually use this as a map I scale this texture 6 times. This however didn’t go as expected:

All the OpenGL code is in my homebrew 2D OpenGL rendering library, and since OpenGL is a state machine it is hard to actually document the whole rendering process. But here is +/- what I do (the code is Python):
width, height = window.get_size()
glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
glOrtho(0.0, width, height, 0.0, 0.0, 1.0)
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glLoadIdentity()
# displacement trick for exact pixelization
glTranslatef(0.375, 0.375, 0.0)
glDisable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(self.texture.target) # GL_TEXTURE_2D
glBindTexture(self.texture.target, self.texture.id)
glTexParameteri(self.texture.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(self.texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glPushMatrix()
glTranslatef(self.x, self.y, 0.0) # self.x and self.y are negative int offsets
glScalef(self.scale_x, self.scale_y, 1.0) # scale_x and scale_y are both 6
glBegin(GL_QUADS)
glTexCoord2i(0, 0)
glVertex2i(0, 0)
glTexCoord2i(0, 1)
glVertex2i(0, self.texture.height)
glTexCoord2i(1, 1)
glVertex2i(self.texture.width, self.texture.height)
glTexCoord2i(self.texture.width, 0)
glVertex2i(self.texture.width, 0)
glEnd()
glPopMatrix()
glDisable(self.texture.target)
However, this “blurring” bug doesn’t occur when I use GL_TEXTURE_RECTANGLE_ARB. I’d like to also be able to use GL_TEXTURE_2D, so can someone please point out how to stop this from happening?
When in doubt – replace your texture with same size black’n’white checkerboard (black/white 1px). It will give you a good sense of what is going – is it going to be uniformly gray (displacement is wrong) or is it going to have waves (scaling is wrong).
Make sure you don’t have mip-maps automatically generated and used.
Generally you don’t need any special displacement, texels match pixels with properly setup glOrtho.
Another important issue – use PowerOfTwo textures, as older GPU could used various schemes to support NPOT textures (scaling or padding transparently to user) which could result in just that sort of blurring.
To manually work with NPOT textures you will need to pad them with clear pixels till next POT size and scale your UV values
glTextCoord2f(u,v)by the factornpotSize / potSize. Note that this is not compatible with tiling, but judging from your art you don’t need it.