2017-11-06 21 views
1

컬러 비트 맵을 회색조로 변환해야합니다. 다음은 접근 방식입니다.C++에서 컬러 비트 맵을 회색조로 변환

 HDC    hdcWindow = GetDC(hWnd); 
     HBITMAP hDIBBitmap; 
     { 
      // Create a compatible DC which is used in a BitBlt from the window DC 
      HDC hdcMemDC = CreateCompatibleDC(hdcWindow); 
      SelectObject(hdcMemDC, bmHatch); 

      BITMAPINFO bmi; 
      memset(&bmi, 0, sizeof(BITMAPINFO)); 
      bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
      bmi.bmiHeader.biWidth = bm.bmWidth; 
      bmi.bmiHeader.biHeight = bm.bmHeight; // top-down 
      bmi.bmiHeader.biPlanes = bm.bmPlanes; 
      bmi.bmiHeader.biBitCount = bm.bmBitsPixel; 
      UINT* pBits; 
      HBITMAP hDIBBitmap = CreateDIBSection(hdcMemDC, &bmi, DIB_RGB_COLORS, (void**)&pBits, NULL, NULL); 
      for (int i = 0; i < bm.bmWidth; i++) { 
       for (int j = 0; j < bm.bmHeight; j++) { 
        UINT val = pBits[i + j * bm.bmWidth]; 
        if (val != 0) 
        { 
         COLORREF clr = val; 
         UINT newVal = (GetRValue(clr) + GetBValue(clr) + GetGValue(clr))/3; 
         pBits[i + j * bm.bmWidth] = newVal; 
        } 
       } 
      } 
      SelectObject(hdcMemDC, hDIBBitmap); 

      // draw content of memory bitmap in the window 
      BitBlt(hdcWindow, 0, 0, bm.bmWidth, bm.bmHeight, hdcMemDC, 0, 0, SRCCOPY); 

      DeleteObject(hDIBBitmap); 
      DeleteDC(hdcMemDC); 
     } 
     ReleaseDC(hWnd, hdcWindow); 

위의 코드 샘플에서 입력 비트 맵은 Bitmap 인스턴스 인 bm으로 제공됩니다.

호환되는 DC를 만들었습니다. selectObject 문을 사용하여 비트 맵을로드합니다. 그런 다음 비트 맵 값을 트래버스하기 위해 DIB 섹션을 만들어 비트를 변경하려고합니다. 값을 변경 한 후 hDIBBitmap이 선택되고 마지막으로 BitBlt 함수를 사용하여 그리기됩니다.

다음 줄을 주석 처리하면 원래 비트 맵이 올바르게 렌더링 된 것을 볼 수 있습니다. SelectObject (hdcMemDC, hDIBBitmap);

내가 관찰 한 것은 pBits가 항상 0이므로 변환 된 그레이 스케일 이미지가 렌더링되지 않고 대신에 검은 색 그림을 얻는 것입니다.

이 접근법의 문제점에 대해 조언 해주십시오.

답변

1

CreateDIBSection은 그 사용법으로 빈 비트 맵을 생성합니다. 원래 비트 맵의 ​​비트는 사용되지 않습니다. 페인트하면 검은 비트 맵이됩니다.

SelectObject(hdcMemDC, hDIBBitmap)을 주석 처리하면이 새로운 hDIBBitmap도 무시됩니다. 이전 장치 컨텍스트에서 선택한 원래 비트 맵을 인쇄합니다. SelectObject(hdcMemDC, bmHatch)

HBITMAP을 회색조로 변환하려면 다음 코드를 사용하십시오. 팔레트 비트 맵에서는 작동하지 않습니다.

변환 24 비트 또는 그레이 스케일 32 비트 HBITMAP :

void grayscale(HBITMAP hbitmap) 
{ 
    BITMAP bm; 
    GetObject(hbitmap, sizeof(bm), &bm); 
    if(bm.bmBitsPixel < 24) 
    { 
     DebugBreak(); 
     return; 
    } 

    HDC hdc = GetDC(HWND_DESKTOP); 
    DWORD size = ((bm.bmWidth * bm.bmBitsPixel + 31)/32) * 4 * bm.bmHeight; 

    BITMAPINFO bmi 
    {sizeof(BITMAPINFOHEADER),bm.bmWidth,bm.bmHeight,1,bm.bmBitsPixel,BI_RGB,size}; 

    int stride = bm.bmWidth + (bm.bmWidth * bm.bmBitsPixel/8) % 4; 
    BYTE *bits = new BYTE[size]; 
    GetDIBits(hdc, hbitmap, 0, bm.bmHeight, bits, &bmi, DIB_RGB_COLORS); 
    for(int y = 0; y < bm.bmHeight; y++) { 
     for(int x = 0; x < stride; x++) { 
      int i = (x + y * stride) * bm.bmBitsPixel/8; 
      BYTE gray = BYTE(0.1 * bits[i+0] + 0.6 * bits[i+1] + 0.3 * bits[i+2]); 
      bits[i+0] = bits[i+1] = bits[i+2] = gray; 
     } 
    } 

    SetDIBits(hdc, hbitmap, 0, bm.bmHeight, bits, &bmi, DIB_RGB_COLORS); 
    ReleaseDC(HWND_DESKTOP, hdc); 
    delete[]bits; 
} 

사용법은 memdc에 HBITMAP 선택하고 GetDIBits 전화를하지만, GetDIBits 문서는 "비트 맵을 말한다

//convert to grayscale 
//this should be called once 
grayscale(hbitmap); 

//paint image 
HDC memdc = CreateCompatibleDC(hdc); 
HBITMAP oldbmp = SelectObject(memdc, hbitmap); 
BITMAP bm; 
GetObject(hbitmap, sizeof(bm), &bm); 
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, memdc, 0, 0, SRCCOPY); 
... 
+2

응용 프로그램이이 함수를 호출 할 때 hbmp 매개 변수에 의해 식별 된 값이 ** 장치 컨텍스트로 선택되지 않아야합니다. " SetDIBits도 마찬가지입니다. 또한 비트 맵이 선택된 상태에서 메모리 DC를 삭제합니다. 나는 당신도 memdc가 필요하다고 생각하지 않는다. –

+1

@AdrianMcCarthy 맞습니다. 나는 그 대답을 편집하고 불필요한'SelectObject'를 제거했다. memdc도 제거했다. 'SetDIBits'에서'HDC' 매개 변수를 0으로 설정할 수 있습니다. 그러나'HDDI' 매개 변수가 0이면'GetDIBits'가 실패합니다. 왜 그런지 이해하지 못합니다! –

+1

좋아 보인다! 그리고 네, 저는 GetDIBits가 HDC를 필요로하지 않아도 HDC를 갖는 것에 대해 매우 까다 롭다고 배웠습니다. https://stackoverflow.com/questions/3962237/what-is-the-hdc-for-in-getdibits –