Kaizen
Kaizen

Reputation: 149

C++ How to get screen shot output in memory with 16 color (4bit) bitmap

I'm trying to get an image with 16 color bitmap.
I can not find a solution on the internet post.

Simple implementation here.

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>

LPBITMAPINFO ConstructBitmapInfo(int nBits, int nX, int nY);
VOID RocketBuffer(LPVOID lpData);

int _tmain(int argc, _TCHAR* argv[])
{
    HWND hDesktop = NULL;
    HDC hCurrenDC = NULL;
    INT nX, nY;
    LPBITMAPINFO lpBmpInfo;
    HBITMAP hBmpScreen;
    LPVOID lpBmpBuffer = NULL;

    if(NULL == (hDesktop = GetDesktopWindow()))
        return GetLastError();

    if (NULL == (hCurrenDC = GetDC(hDesktop)))
        return GetLastError();

    nX = GetSystemMetrics(SM_CXSCREEN);
    nY = GetSystemMetrics(SM_CYSCREEN);

    HDC hOrgMemDC = NULL;
    hOrgMemDC = CreateCompatibleDC(hCurrenDC);
    lpBmpInfo = ConstructBitmapInfo(4, nX, nY);
    hBmpScreen = CreateDIBSection(hCurrenDC, lpBmpInfo, DIB_RGB_COLORS, &lpBmpBuffer, NULL, 0);

    SelectObject(hOrgMemDC, hBmpScreen);

    if(!BitBlt(hOrgMemDC, 0, 0, nX, nY, hCurrenDC, 0, 0, SRCCOPY))
    {
        return GetLastError();
    }

    LPBYTE lpWriteData = new BYTE[lpBmpInfo->bmiHeader.biSizeImage * 2];

    memcpy(lpWriteData, lpBmpBuffer, lpBmpInfo->bmiHeader.biSizeImage);
    RocketBuffer(lpBmpBuffer);

    delete[] lpWriteData;

    return 0;
}

LPBITMAPINFO ConstructBitmapInfo(int nBits, int nX, int nY)
{   
    int color_num = nBits <= 8 ? 1 << nBits : 0;

    int nBISize = sizeof(BITMAPINFOHEADER) + (color_num * sizeof(RGBQUAD));
    BITMAPINFO  *lpbmi = (BITMAPINFO *) new BYTE[nBISize];

    BITMAPINFOHEADER    *lpbmih = &(lpbmi->bmiHeader);
    lpbmih->biSize = sizeof(BITMAPINFOHEADER);
    lpbmih->biWidth = nX;
    lpbmih->biHeight = nY;
    lpbmih->biPlanes = 1;
    lpbmih->biBitCount = nBits;
    lpbmih->biCompression = BI_RGB;
    lpbmih->biXPelsPerMeter = 0;
    lpbmih->biYPelsPerMeter = 0;
    lpbmih->biClrUsed = 0;
    lpbmih->biClrImportant = 0;
    lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight;

    if (nBits >= 16)
        return lpbmi;

    HDC hDC = GetDC(NULL);
    HBITMAP hBmp = CreateCompatibleBitmap(hDC, 1, 1); 
    GetDIBits(hDC, hBmp, 0, 0, NULL, lpbmi, DIB_RGB_COLORS);
    ReleaseDC(NULL, hDC);
    DeleteObject(hBmp);

    return lpbmi;
}

VOID RocketBuffer(LPVOID lpData)
{
    // ...
}

I can not output lpWriteData as a * .bmp file. It's not BMP format.
Therefore, the function RocketBuffer (LPVOID lpData) will not be able to perform its function correctly.

The problem is how to get screen shot output in memory with 16 color bitmap.

32 Bit Color Image
32 Bit Color Image

I want 4 bit image like this.
4 Bit Color Image
4 Bit Color Image

Upvotes: 2

Views: 731

Answers (1)

Barmak Shemirani
Barmak Shemirani

Reputation: 31599

16 color 4-bit image includes a color table with 16 colors. The color table size is 16 * 4 bytes. BITMAPINFO should be large enough to receive the color palette through GetDIBits

The code below works for the following bitmaps:

  • 1-bit bitmaps (monochrome) //bpp = 1
  • 4-bit bitmaps (16 colors) //bpp = 4
  • 8-bit bitmaps (256 colors) //bpp = 8

.

#include <iostream>
#include <fstream>
#include <vector>
#include <windows.h>

int main()
{
    //4-bit bitmap: bpp = 4
    //valid values with this method bpp = 1, 4, 8
    WORD bpp = 4;

    //color table:
    int colorsize = (1 << bpp) * sizeof(RGBQUAD);

    int width = GetSystemMetrics(SM_CXFULLSCREEN);
    int height = GetSystemMetrics(SM_CYFULLSCREEN);

    HDC hdc = GetDC(HWND_DESKTOP);
    HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height);
    HDC memdc = CreateCompatibleDC(hdc);
    HGDIOBJ oldbmp = SelectObject(memdc, hbitmap);
    BitBlt(memdc, 0, 0, width, height, hdc, 0, 0, CAPTUREBLT | SRCCOPY);
    SelectObject(memdc, oldbmp);

    //size in bytes for pixel data:
    DWORD size = ((width * bpp + 31) / 32) * 4 * height;

    std::vector<BYTE> bi_memory;
    bi_memory.resize(sizeof(BITMAPINFOHEADER) + colorsize, 0);
    BITMAPINFO* bi = (BITMAPINFO*)&bi_memory[0];
    bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bi->bmiHeader.biWidth = width;
    bi->bmiHeader.biHeight = height;
    bi->bmiHeader.biPlanes = 1;
    bi->bmiHeader.biBitCount = bpp;
    bi->bmiHeader.biCompression = BI_RGB;
    bi->bmiHeader.biClrUsed = 16;

    std::vector<BYTE> pixels(size + colorsize);
    GetDIBits(hdc, hbitmap, 0, height, &pixels[0], bi, DIB_RGB_COLORS);

    std::ofstream fout(TEXT("c:\\test\\_4bit.bmp"), std::ios::binary);
    if(fout)
    {
        //bitmap file header 
        //(54 = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))
        BITMAPFILEHEADER filehdr = { 'MB', 54 + colorsize + size, 0, 0, 54 };
        fout.write((char*)&filehdr, sizeof(BITMAPFILEHEADER));

        //bitmap info header
        fout.write((char*)&bi->bmiHeader, sizeof(BITMAPINFOHEADER));

        //color table
        fout.write((char*)bi->bmiColors, colorsize);

        //pixel data
        fout.write((char*)pixels.data(), pixels.size());
    }

    //cleanup:
    DeleteObject(memdc);
    DeleteObject(hbitmap);
    ReleaseDC(HWND_DESKTOP, hdc);

    return 0;
}

Upvotes: 3

Related Questions