If you have OpenGL 4.3 support, there is a direct glCopyImageSubData for this purpose:
glCopyImageSubData(tex1, GL_TEXTURE_2D, 0, 0, 0, 0, tex2, GL_TEXTURE_2D, 0, 0, 0, 0, width, height, 1);
Of course, this requires that the destination texture be already selected with an image of the appropriate size and format (using a simple glTexImage2D(..., nullptr) , and maybe even better than glTexStorage2D if you have GL 4).
If you donโt have this, then rendering one texture to another using FBO might be the best approach. In the end, you donโt even need to display the original texture. You can simply attach both textures to FBO and add a one-color application to another using glBlitFramebuffer (the core with OpenGL 3 or with the GL_EXT_framebuffer_blit extension in 2.x, almost anywhere where FBOs are in the first place):
glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex1, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex2, 0); glDrawBuffer(GL_COLOR_ATTACHMENT1); glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
Of course, if you do this several times, it might be a good idea to keep this FBO alive. And similarly, it also requires that the destination texture image has the appropriate size and format in advance. Or you can also use Michael's suggestion to combine the source texture with FBO and the good old glCopyTex(Sub)Image2D into the target texture. It is necessary to evaluate what is better (if any).
And if you donโt even have this, you can still use your approach to read one texture and write this data to another. But instead of using processor memory as a temporary buffer, use a pixel buffer object (PBO) (kernel with OpenGL 2.1). You will still have an extra copy, but at least it will (or most likely will) be a copy of the GPU-GPU.