save 24 bit mode screen to file in 8 bit mode

My basic question is:

-the video card is in 16 or 24 bit mode,
-less than 256 colours are dispayed (some windows colours, some my own),

How can I save (part of) the screen to file in 8 bit mode?

The following approach gives the wrong colours (some kind of standard palette with a number of shades of a number of colours):

ScreenDC     := GetDc(0);
MemDc        := CreateCompatibleDc(ScreenDC);
MemBitmap    := CreateCompatibleBitmap(ScreenDC, SrcRect.width, SrcRect.height);
OldMemBitmap := SelectObject(MemDc, MemBitmap);

BitBlt(MemDC, ..., SrcDC, ...);  //SrcDC is the dc of the form

Then I fill a DIB info header with the format I want the bitmap: 1 plane, 8 bits per pixel

and then I call GetDIBits twice: the first time to get the colours into the header, and then to get the bits:

GetDIBits(MemDC, MemBitmap, 0, SrcRect.Height, nil,
      PBitmapInfo(pDibHeader)^, DIB_RGB_COLORS);

GetDIBits(MemDC, MemBitmap, 0, SrcRect.Height, pBits,
      PBitmapInfo(pDibHeader)^, DIB_RGB_COLORS);

After the first GetDIBits, the header does not contain my colours, but some sort of standard colour table, containing a couple of shades of every base colour. Saving the bitmap to file and viewing it with MS Paint indeed shows the wrong colours.
This approach works fine in 8 bit screen mode, but I need to handle screens in higher modes as well.

So my question is: what do I have to do to get the correct colours?

Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

cadenzaAuthor Commented:
Edited text of question

Why not try the following:
procedure SaveDesktop;
    Bmp: TBitmap;
    DC: HDC;
    Bmp := TBitmap.Create;
    DC := GetDC(0);
    Bmp.Width := Screen.Width;
    Bmp.Height := Screen.Height;
    BitBlt(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, DC, 0, 0, SRCCOPY);
    ReleaseDC(0, DC);

If that doesn't work, try the following before Bmp.SaveToFile:
Bmp.PixelFormat := pf8bit;
(I think that PixelFormat will only work on Delphi 3...)

cadenzaAuthor Commented:
Hi Jim Bob,

thanks for your suggestion.

1- if you let Delphi create a bitmap, it makes it compatible with the screen. So if your video card is in 24 bit mode, it makes a 24 bit bitmap. Saving this to file gives a 24 bit mode file.

2- pf8bit is Delphi 3. I use Delphi 2. (if this works in D3, which I think it does not, I would be very curious to learn how they do it).

I think my question boils down to: how can I make Windoze derive a proper RGB table (<= 256 entries) from the screen/a bitmap.

Exploring SQL Server 2016: Fundamentals

Learn the fundamentals of Microsoft SQL Server, a relational database management system that stores and retrieves data when requested by other software applications.

Windows does not store "palettes" in any mode higher than 8-bit.  All pixels are stored in (R,G,B).  This is just a guess but you're probably going to have to create your own palette structure and translate the internal (R,G,B) pixels into palette mapped pixels.
I wrote some code to do this, to save GIF files from a 24bit bitmap. Essentially its what grrendot said.

The only way I have found to convert True Color bitmap to a palette based bitmap is to create a palette by going through every pixel and adding each new color to the palette, and setting the value in the destination bitmap to the palette index of the color.

Yep, its slow, one way to optimise is to remember last palette index when looking up a pixel, as adjacent pixels are often the same. Also if you have more colors in the source bitmap than entries in the palette, you will have to throw the lesser used ones away, and map them to similar colors, or dither.

Not easy.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
cadenzaAuthor Commented:
Hi Greendot & Ergates,

thanks for your comment/answer. I think I'll have to do what you say, but I thought of taking a shortcut about which I would like your (or anyone else's) opinion.

My advantage is: I know the colours that are present in the source: there are a couple of bitmaps of which I set the colours myself + some gray around it from the window's background + black and white. So do you think it would work if I loaded these colours into the RGB quad entries of the DIB header explicitly before calling GetDIBits to get the 8-bit pixels. In other words: is Windoze so clever to map the 24 bit pixels to the right indices of the colours in the header? If that would be the case, my problem seems to be solved, I guess.

Unfortunately not, Windoze is not at all clever!
cadenzaAuthor Commented:
That is a pitty, although it answers my question. I'll follow your approach and see if I can live with the speed. Maybe I can take advantage of the fact that I already know the colours used.. Or I simply force my users to switch to 8 bit colour :) .

thanks again for your answer,

It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.