Problem with SetDIBColorTable

Hello,

I have a class member method which reads a bitmap from file, then creates a windows bitmap containing the same data. If it's an 8-, 4-, or 1-bit bitmap, in addition to using SetDIBits() to set the bitmap data, I am trying to use SetDIBColorTable() to set the color table data.

My code is shown below. Even though I handle 32-, 4-, and 1-bit images as well, I have included only the 24- and 8-bit portions, and taken out all error checks to make the code more readable here. The only part that fails is the SetDIBColorTable() function anyway. In any case, the image is otherwise valid and I am able to display it, although the colors, of course, come out wrong in the 8-bit image. Calling GetLastError() after SetDIBColorTable() returns Error Code 6, which indicates an invalid handle. I'm not sure if that refers to the bitmap handle or the DC handle, I have to assume for now that the DC is the problem, because, like I said, displaying the image works fine, except that the colors are off. Any ideas? I have never tried to use the SetDIBColorTable() function before. Thanks in advance...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
void Bitmap::load(const string& filename) {

    //Create a binary input stream
    ifstream inputStream(filename.c_str(),ios::binary);
	
    //Advance to last position to get file size
    inputStream.seekg(0,ios::end);
    unsigned fileSize = inputStream.tellg();

    //Rewind to beginning
    inputStream.seekg(0,ios::beg);

    //Create file header
    BITMAPFILEHEADER bmfh;

    //Read the file header fields from file
    inputStream.read((char*)&bmfh.bfType,sizeof(bmfh.bfType));
    inputStream.read((char*)&bmfh.bfSize,sizeof(bmfh.bfSize));
    inputStream.read((char*)&bmfh.bfReserved1,sizeof(bmfh.bfReserved1));
    inputStream.read((char*)&bmfh.bfReserved2,sizeof(bmfh.bfReserved2));
    inputStream.read((char*)&bmfh.bfOffBits,sizeof(bmfh.bfOffBits));

    //Create info header
    BITMAPINFOHEADER bmih;

    //Read the info header fields from file
    inputStream.read((char*)&bmih.biSize,sizeof(bmih.biSize));
    inputStream.read((char*)&bmih.biWidth,sizeof(bmih.biWidth));
    inputStream.read((char*)&bmih.biHeight,sizeof(bmih.biHeight));
    inputStream.read((char*)&bmih.biPlanes,sizeof(bmih.biPlanes));
    inputStream.read((char*)&bmih.biBitCount,sizeof(bmih.biBitCount));
    inputStream.read((char*)&bmih.biCompression,sizeof(bmih.biCompression));
    inputStream.read((char*)&bmih.biSizeImage,sizeof(bmih.biSizeImage));
    inputStream.read((char*)&bmih.biXPelsPerMeter,sizeof(bmih.biXPelsPerMeter));
    inputStream.read((char*)&bmih.biYPelsPerMeter,sizeof(bmih.biYPelsPerMeter));
    inputStream.read((char*)&bmih.biClrUsed,sizeof(bmih.biClrUsed));
    inputStream.read((char*)&bmih.biClrImportant,sizeof(bmih.biClrImportant));

    //Declare necessary variables
    unsigned nColorBytes;		//Number of entries in color index array, or length of BGR/BGRA array
    unsigned nTableEntries;		//Number of entries in the color table
    vector<BYTE> colorBytes;		//Color table index array, or literal BGR/BGRA byte array
    vector<RGBQUAD> colorTable;		//Array of color values

    //Size the arrays according to bit depth
    switch (bmih.biBitCount) {
        case 24:
        {
            //Calculate end-of-line byte padding
            unsigned padding = 0;
            while ((bmih.biWidth * 3 + padding) % 4 != 0) {
                ++padding;
            }

            //Set sizes
            nColorBytes = bmih.biHeight * (bmih.biWidth * 3 + padding);
            nTableEntries = 0;
	    break;
	}

        case 8:
        {
            //Calculate end-of-line byte padding
            unsigned padding = 0;
            while ((bmih.biWidth + padding) % 4 != 0) {
                ++padding;
            }
			
            //Set sizes
            nColorBytes = bmih.biHeight * (bmih.biWidth + padding);
            nTableEntries = (bmih.biClrUsed == 0)? 256 : bmih.biClrUsed;
            break;
        }

        default:
            ::MessageBox(NULL,"Unsupported bit depth!","",0);	
            inputStream.close();
            return; 
	
    }

    //Allocate memory
    colorBytes.resize(nColorBytes);
    colorTable.resize(nTableEntries);

    //Read the color table entries one-by-one
    for (unsigned i=0; i<nTableEntries; i++) {
    	inputStream.read((char*)&colorTable[i],sizeof(RGBQUAD));
    }

    //Read the array color indices (or literal BGR components) 
    inputStream.read((char*)&colorBytes[0],nColorBytes);

    //Close the input file
    inputStream.close();

    //Get DC for SetDIBits to use
    HDC hdcScreen = ::GetDC(NULL);
	
    //Create and initialize the BITMAPINFO structure for SetDIBits to use
    BITMAPINFO bmi;
    bmi.bmiHeader = bmih;  //Use the header information from the input file

    //Create the new bitmap
    HBITMAP hNewBitmap = ::CreateCompatibleBitmap(hdcScreen,bmih.biWidth,bmih.biHeight);

    //Transfer the color byte array into the bitmap
    ::SetDIBits(hdcScreen,hNewBitmap,0,bmih.biHeight,&colorBytes[0],&bmi,DIB_RGB_COLORS);

    //Set the color table, if one exists
    if (nTableEntries > 0) {
        //Create compatible DC to select image into for SetDIBColorTable
        HDC hdcTemp = ::CreateCompatibleDC(hdcScreen);
	
        //Select the new bitmap into the color table DC, and save pointer to original
        HBITMAP hbmOldTemp = (HBITMAP)::SelectObject(hdcTemp,hNewBitmap);

        //Set the color table, starting with the first entry
        ::SetDIBColorTable(hdcTemp,0,nTableEntries,&colorTable[0]); //FAILS
		
        //Restore temp DC's original bitmap, and free memory
        ::SelectObject(hdcTemp,hbmOldTemp);
        ::DeleteDC(hdcTemp);
    }

    //Restore the screen DC
    ::ReleaseDC(NULL,hdcScreen);	

    //Free any existing member bitmap
    if (m_hBitmap != NULL) {
        ::DeleteObject(m_hBitmap);
    }
    //Replace member bitmap with new one
    m_hBitmap = hNewBitmap;
}
Last edited on
HBITMAP hNewBitmap = ::CreateCompatibleBitmap(hdcScreen,bmih.biWidth,bmih.biHeight);

Assuming your machine is running in 32-bit color, this generates a 32-bit bitmap (ie: compatible with the current display)

Thusly, you're putting a 32-bit bitmap in your hdcTemp, which is why you're getting errors when you try to set the palette. 32-bit bitmaps don't have palettes.

Don't use CreateCompatibleBitmap here. Use CreateBitmap instead:

 
HBITMAP hNewBitmap = ::CreateBitmap( width, height, 1, bit_depth, NULL );


Also note that you no longer really need hdcScreen for this. CreateCompatibleDC() will work fine with NULL as the parameter, and I don't think the DC for SetDIBits is used unless you use DIB_PAL_COLORS (but don't quote me on that -- try passing NULL and see if it works).

Anyway.. since you don't need the screen DC you don't have to lock/get it. Which is definately a performance plus.

EDIT: man.. at work I have to type "pallet" all the time, but in this case it's spelled palette. Aye ya.
Last edited on
Thanks - that clears up a few things all at once.
On a side note:

Since you're not doing any bitmap conversion here, you might want to consider using CreateDIBSection instead of CreateBitmap.

CreateDIBSection gives you a direct pointer to the pixel data so you can read from the file directly to the bitmap without having to allocate and copy from an intermediate buffer.
Topic archived. No new replies allowed.