mmr
mmr

Reputation: 14919

glDrawPixels/glCopyPixels to get a magnified view producing severely clamped image

Despite an earlier question (asked here), our project is constrained to using glDrawPixels, so we have to do some hackery.

One of the feature requirements is to be able to have a magnified view show up on a clicked region of an image; so, looking at an image, I want to click the mouse, and have a 200% image window show up where the mouse is. As I drag my cursor, the window should follow the cursor.

The context is set up like:

The Big Red Book has code that looks like this:

        Gl.glShadeModel(Gl.GL_FLAT);
        Gl.glClearColor(0.1f, 0.1f, 0.1f, 0.0f);
        Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 2);
        Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_LINE);

        Gl.glDisable(Gl.GL_SCISSOR_TEST);
        Gl.glDisable(Gl.GL_ALPHA_TEST);
        Gl.glDisable(Gl.GL_STENCIL_TEST);
        Gl.glDisable(Gl.GL_DEPTH_TEST);
        Gl.glDisable(Gl.GL_BLEND);
        Gl.glDisable(Gl.GL_DITHER);
        Gl.glDisable(Gl.GL_LOGIC_OP);
        Gl.glDisable(Gl.GL_LIGHTING);
        Gl.glDisable(Gl.GL_FOG);
        Gl.glDisable(Gl.GL_TEXTURE_1D);
        Gl.glDisable(Gl.GL_TEXTURE_2D);
        Gl.glPixelTransferi(Gl.GL_MAP_COLOR, Gl.GL_TRUE);
        Gl.glPixelTransferf(Gl.GL_RED_SCALE, 1.0f);
        Gl.glPixelTransferi(Gl.GL_RED_BIAS, 0);
        Gl.glPixelTransferf(Gl.GL_GREEN_SCALE, 1.0f);
        Gl.glPixelTransferi(Gl.GL_GREEN_BIAS, 0);
        Gl.glPixelTransferf(Gl.GL_BLUE_SCALE, 1.0f);
        Gl.glPixelTransferi(Gl.GL_BLUE_BIAS, 0);
        Gl.glPixelTransferi(Gl.GL_ALPHA_SCALE, 1);
        Gl.glPixelTransferi(Gl.GL_ALPHA_BIAS, 0);

And then the call to make the smaller-but-zoomed image looks like

        int width = (int)((this.Width * 0.2)/2.0);
        Gl.glReadBuffer(Gl.GL_FRONT_AND_BACK);
        Gl.glRasterPos2i(0, 0);
        Gl.glBitmap(0, 0, 0, 0, mStartX - (width*2), mStartY, null);

        Gl.glPixelZoom(2.0f, 2.0f);
        Gl.glCopyPixels(mStartX - width, mStartY, width, width, Gl.GL_COLOR);

where mStartY and mStartX are the points where the click happened.

Problem is, the window that shows up is really mangling the lookup tables, and really clamping the image down to essentially a black-and-white binary image (ie, no shades of grey).

The data is a black-and-white unsigned short array, and is set with this code:

        float step = (65535.0f / (float)(max - min));
        mColorTable = new ushort[65536];
        int i;
        for (i = 0; i < 65536; i++)
        {
            if (i < min)
                mColorTable[i] = 0;
            else if (i > max)
                mColorTable[i] = 65535;
            else
                mColorTable[i] = (ushort)((float)(i - min) * step);
        }       
        .... //some irrelevant code

        Gl.glPixelMapusv(Gl.GL_PIXEL_MAP_R_TO_R, 65536, mColorTable);
        Gl.glPixelMapusv(Gl.GL_PIXEL_MAP_G_TO_G, 65536, mColorTable);
        Gl.glPixelMapusv(Gl.GL_PIXEL_MAP_B_TO_B, 65536, mColorTable);

Now, according to this documentation, I should use GL_PIXEL_MAP_I_TO_I and set INDEX_SCALE and INDEX_BIAS to zero, but doing that does not change the result, that the image is severely clamped. And by 'severely clamped' I mean it's either black or white, with very few shades of grey, but the original non-magnified image looks like what's expected.

So, how do I avoid the clamping of the magnified view? Should I make a second control that follows the cursor and gets filled in with data from the first control? That approach seems like it would take the array copies outside of the graphics card and into C#, which would almost by definition be slower, and so make the control nonresponsive.

Oh, I'm using C# and the Tao framework, if that matters.

Upvotes: 0

Views: 2977

Answers (3)

mmr
mmr

Reputation: 14919

Here's the answer. The problem is that the LUT is being applied twice, so before calling the copy, call:

Gl.glPixelTransferi(Gl.GL_MAP_COLOR, Gl.GL_FALSE);

Then, once done, call:

Gl.glPixelTransferi(Gl.GL_MAP_COLOR, Gl.GL_TRUE);

That way, the 'extreme clamping' I was describing is removed.

@thing2k-- your solution causes the copy to happen outside the graphics card, so slows down the drawing on mouse drag, but doesn't fix the double clamp.

Upvotes: 2

thing2k
thing2k

Reputation: 608

If I understand you correctly then this should be close to what you after, using glReadPixels and glDrawPixels.

Sorry it's C++ not C# but the OpenGL function should still be the same.

// main.cpp
// glut Text

#ifdef __WIN32__
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
#endif
#include <GL/glut.h>
#include <cstdio>

int WIDTH = 800;
int HEIGHT = 600;
int MouseButton, MouseY = 0, MouseX = 0;
const int size = 80;
char *image, rect[size*size*3];
int imagewidth, imageheight;

bool Init()
{
    int offset;
    FILE* file = fopen("image.bmp", "rb");
    if (file == NULL)
        return false;
    fseek(file, 10, SEEK_SET);
    fread(&offset, sizeof(int), 1, file);
    fseek(file, 18, SEEK_SET);
    fread(&imagewidth, sizeof(int), 1, file);
    fread(&imageheight, sizeof(int), 1, file);
    fseek(file, offset, SEEK_SET);
    image = new char[imagewidth*imageheight*3];
    if (image == NULL)
        return false;
    fread(image, 1, imagewidth*imageheight*3, file);
    fclose(file);
    return true;
}

void Reshape(int width, int height)
{
    WIDTH = width;
    HEIGHT = height;
    glViewport(0 , 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, width, 0, height);
}

void Display()
{
    int size2 = size/2;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glRasterPos2i(0,0);
    glPixelZoom(1.f, 1.f);
    glDrawPixels(imagewidth, imageheight, 0x80E0/*GL_RGB*/, GL_UNSIGNED_BYTE, image);
    glReadPixels(MouseX-size2, MouseY-size2, size, size, GL_RGB, GL_UNSIGNED_BYTE, rect);
    glPixelZoom(2.f, 2.f);
    glRasterPos2i(MouseX-size, MouseY-size);
    glDrawPixels(size, size, GL_RGB, GL_UNSIGNED_BYTE, rect);
    glFlush();
    glutSwapBuffers();
}

void Mouse(int button, int state, int x, int y)
{
    if (state == GLUT_DOWN)
        MouseButton &= (1<<button);
    else
        MouseButton &= ~(1<<button);
}

void MouseMove(int x, int y)
{
    MouseX = x;
    MouseY = HEIGHT - y;
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    if (Init() == false)
        return 1;
    glutInitWindowSize(WIDTH, HEIGHT);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutCreateWindow("glut_Text");
    glClearColor(0.25, 0.25, 0.25, 1.0);
    glutReshapeFunc(Reshape);
    glutDisplayFunc(Display);
    glutIdleFunc(Display);
    glutMouseFunc(Mouse);
    glutMotionFunc(MouseMove);
    glutPassiveMotionFunc(MouseMove);

    glutMainLoop();
    return 0;
}

Hope this helps.

Upvotes: 0

unwind
unwind

Reputation: 399823

Please, pretty please with loads of sugar, molasses, sprinkles and a mist of high-fructose corn syrup on top and all over, explain why you cannot just use texture-mapping to draw this imagery.

Texture-mapping is a core, basic, everyday, run of the mill, garden-variety, standard, typical, expected, and just generally nice feature of OpenGL. It is in version 1.4. Why not use it as a starting point?

Upvotes: 0

Related Questions