2017-05-27 31 views
2

這是我最後一個關於將截圖保存到SOIL的問題的延續。 here現在我想知道,如何製作屏幕截圖部分並消除奇怪行爲的原因。我的代碼:通過SOIL保存位圖時斷開BMP。截圖區

bool saveTexture(string path, glm::vec2 startPos, glm::vec2 endPos) 
{ 
    const char *charPath = path.c_str(); 

    GLuint widthPart = abs(endPos.x - startPos.x); 
    GLuint heightPart = abs(endPos.y - startPos.y); 

    BITMAPINFO bmi; 
    auto& hdr = bmi.bmiHeader; 
    hdr.biSize = sizeof(bmi.bmiHeader); 
    hdr.biWidth = widthPart; 
    hdr.biHeight = -1.0 * heightPart; 
    hdr.biPlanes = 1; 
    hdr.biBitCount = 24; 
    hdr.biCompression = BI_RGB; 
    hdr.biSizeImage = 0; 
    hdr.biXPelsPerMeter = 0; 
    hdr.biYPelsPerMeter = 0; 
    hdr.biClrUsed = 0; 
    hdr.biClrImportant = 0; 

    unsigned char* bitmapBits = (unsigned char*)malloc(3 * widthPart * heightPart); 

    HDC hdc = GetDC(NULL); 
    HDC hBmpDc = CreateCompatibleDC(hdc); 
    HBITMAP hBmp = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**)&bitmapBits, nullptr, 0); 
    SelectObject(hBmpDc, hBmp); 
    BitBlt(hBmpDc, 0, 0, widthPart, heightPart, hdc, startPos.x, startPos.y, SRCCOPY); 

    //UPDATE: 
    - int bytes = widthPart * heightPart * 3; 
    - // invert R and B chanels 
    - for (unsigned i = 0; i< bytes - 2; i += 3) 
    - { 
    - int tmp = bitmapBits[i + 2]; 
    - bitmapBits[i + 2] = bitmapBits[i]; 
    - bitmapBits[i] = tmp; 
    - } 

    + unsigned stride = (widthPart * (hdr.biBitCount/8) + 3) & ~3; 
    + // invert R and B chanels 
    + for (unsigned row = 0; row < heightPart; ++row) { 
    +  for (unsigned col = 0; col < widthPart; ++col) { 
    +   // Calculate the pixel index into the buffer, taking the 
      alignment into account 
    +   const size_t index{ row * stride + col * hdr.biBitCount/8 }; 
    +   std::swap(bitmapBits[index], bitmapBits[index + 2]); 
    +  } 
    + } 

    int texture = SOIL_save_image(charPath, SOIL_SAVE_TYPE_BMP, widthPart, heightPart, 3, bitmapBits); 

    return texture; 
} 

當我運行這個,如果widthPart和heightPart是偶數,那就很完美了。但是,如果從這個事情是奇數我得到這個BMP的:

Broken BMP1

Broken BMP2

我檢查任何轉換和代碼的兩倍,但在我看來,原因是我的錯塊傳輸功能。轉換RGB的功能不會影響問題。什麼可能是一個原因?這是BitBlt中區域的正確方法?

更新無偶數或奇數。當這個數字相等時,產生正確的圖像。我不知道哪裏出了問題((

UPDATE2

SOIL_save_image功能檢查參數錯誤併發送至stbi_write_bmp

int stbi_write_bmp(char *filename, int x, int y, int comp, void *data) 
{ 
    int pad = (-x*3) & 3; 
    return outfile(filename,-1,-1,x,y,comp,data,0,pad, 
     "11 4 22 4" "4 44 22 444444", 
     'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 
     40, x,y, 1,24, 0,0,0,0,0,0);    // bitmap header 
} 

OUTFILE功能:

static int outfile(char const *filename, int rgb_dir, int vdir, int x, int 
y, int comp, void *data, int alpha, int pad, char *fmt, ...) 
{ 
    FILE *f = fopen(filename, "wb"); 
    if (f) { 
     va_list v; 
     va_start(v, fmt); 
     writefv(f, fmt, v); 
     va_end(v); 
     write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); 
     fclose(f); 
    } 
    return f != NULL; 
} 

回答

4

破碎的位圖圖像是Windows位圖之間的數據佈局不一致以及SOIL庫預計的結果。從CreateDIBSection返回像素緩衝器遵循的Windows規則(見Bitmap Header Types):

掃描線對齊DWORD [...]。它們必須填充掃描線寬度,以字節爲單位,這些寬度不能被4 [...]整除。

換言之:每條掃描線的寬度(以字節爲單位)爲(biWidth * (biBitCount/8) + 3) & ~3。另一方面,SOIL庫不希望像素緩衝區是DWORD對齊的。

爲了解決這個問題,像素數據在傳遞到SOIL之前需要進行轉換,通過剝離(潛在)填充和交換R和B顏色通道。下面的代碼這樣做就地:

unsigned stride = (widthPart * (hdr.biBitCount/8) + 3) & ~3; 

for (unsigned row = 0; row < heightPart; ++row) { 
    for (unsigned col = 0; col < widthPart; ++col) { 
     // Calculate the source pixel index, taking the alignment into account 
     const size_t index_src{ row * stride + col * hdr.biBitCount/8 }; 
     // Calculate the destination pixel index (no alignment) 
     const size_t index_dst{ (row * width + col) * (hdr.biBitCount/8) }; 
     // Read color channels 
     const unsigned char b{ bitmapBits[index_src] }; 
     const unsigned char g{ bitmapBits[index_src + 1] }; 
     const unsigned char r{ bitmapBits[index_src + 2] }; 
     // Write color channels switching R and B, and remove padding 
     bitmapBits[index_dst] = r; 
     bitmapBits[index_dst + 1] = g; 
     bitmapBits[index_dst + 2] = b; 
    } 
} 

有了這個代碼,index_src是索引到像素緩衝器,其包括填充以執行適當的DWORD對準。 index_dst是沒有應用任何填充的索引。將像素從index_src移動到index_dst可刪除(潛在)填充。


泄密的符號是掃描線移動到左邊或右邊被一個或兩個像素(或以不同的速度各個顏色通道)。這通常是一個安全的跡象,即掃描線對齊不一致。
該操作是破壞性的,即,像素緩衝器不再被傳遞到Windows GDI函數一旦被轉換,儘管原始數據可以 被重建,即使有點更復雜。

+0

無論如何,謝謝你的幫助,但是我嘗試了不同的變體代碼(在編輯之前),它沒有任何改變。 :((每次當widthPart不能被4整除時(不管是什麼是heightPart)我得到的是BMP的上面,我用你的部分更新了這個問題 – hardCode

+0

@hardCode:請不要接受一個不能解決你的問題的答案,並且不要編輯你的問題使它變成另外一個,而是發表評論來要求澄清。在猜測中,我會假設,這個問題仍然與位圖圖像中的掃描線對齊有關,也許'SOIL_save_image'沒有我們可以看到'SOIL_save_image'的文檔嗎? – IInspectable

+0

我更新了這個問題。在SOIL文檔中沒有關於編寫BMP的內容:http://www.lonesock.net/soil.html – hardCode