C: Проблема WinAPI CreateDIBitmap() из byte[]

Я уже некоторое время работаю над этой проблемой.
Я пытаюсь добавить поддержку JPEG в программу с помощью libjpeg.
По большей части это работает довольно хорошо, но для некоторых файлов JPEG они появляются как на картинке слева.

(Сравните с исходным изображением.)

Это может быть неочевидно, но фон отображается чередующимися красными, зелеными и синими рядами. Если кто-то видел это поведение раньше и знает возможную причину, я был бы признателен за любой вклад.

Я дополнил строки так, чтобы они были кратны четырем байтам, и это лишь немного помогло решить проблему.

Код:

  rowSize = cinfo.output_width * cinfo.num_components;
  /* Windows needs bitmaps to be defined on Four Byte Boundaries */
  winRowSize = (rowSize + 3) & -4;
  imgSize = (cinfo.output_height * winRowSize + 3) & -4;
  while(cinfo.output_scanline < cinfo.output_height){
        jpeg_read_scanlines(&cinfo, &row_pointer, 1);

        /* stagger read to get lines Bottom->Top (As BMP Requires) */
        location = (imgSize) - (cinfo.output_scanline * winRowSize);
        rowsRead++;

        for(i = 0; i < winRowSize; i++){
           rawImage[location++] = row_pointer[i];
        }
     }

     /* Convert BGR to RGB */
     if(cinfo.num_components == 3){
        for(i = 0; i < imgSize; i += 3){
           tmp = rawImage[i+2];
           rawImage[i+2] = rawImage[i];
           rawImage[i] = tmp;
        }
     }

     biSize = sizeof(BITMAPINFOHEADER);
     if(cinfo.num_components == 1){ /* Greyscale */
        biPallete = 32 * 256;
        biSize += biPallete;
     }

     bitInf = (BITMAPINFO *)malloc(biSize);

     bitInf->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
     bitInf->bmiHeader.biWidth = cinfo.output_width;
     bitInf->bmiHeader.biHeight = cinfo.output_height;
     bitInf->bmiHeader.biPlanes = 1;
     bitInf->bmiHeader.biBitCount = 8*cinfo.num_components;
     bitInf->bmiHeader.biCompression = BI_RGB;
     bitInf->bmiHeader.biSizeImage = 0;
     bitInf->bmiHeader.biXPelsPerMeter = 0;
     bitInf->bmiHeader.biYPelsPerMeter = 0;
     bitInf->bmiHeader.biClrUsed       = 0;
     bitInf->bmiHeader.biClrImportant  = 0;

     if(cinfo.num_components == 1){
        for(i = 0; i < 256; i++){
           bitInf->bmiColors[i].rgbBlue = i;
           bitInf->bmiColors[i].rgbGreen = i;
           bitInf->bmiColors[i].rgbRed = i;
           bitInf->bmiColors[i].rgbReserved = 0;
        }
     }

     /* Loads rawImage into an HBITMAP */
     /* retval = CreateDIBitmap(inDC, &bitInf->bmiHeader, CBM_INIT, rawImage, bitInf, DIB_RGB_COLORS); */
     retval = CreateCompatibleBitmap(inDC, cinfo.output_width, cinfo.output_height);
     errorCode = SetDIBits(inDC, retval, 0, cinfo.output_height, rawImage, bitInf, DIB_RGB_COLORS);

Решение: я изменил преобразователь RGB/BGR на это:

if(cinfo.num_components == 3){
   for(i = 0; i < cinfo.output_height; i++){
      location = (i * winRowSize);
      for(j = 0; j < rowSize; j += 3){
         tmp = rawImage[location+2];
         rawImage[location+2] = rawImage[location];
         rawImage[location] = tmp;
         location += 3;
      }
   }
}

И это сработало как шарм. Благодаря roygbiv.


person zmbush    schedule 26.03.2010    source источник
comment
Возможно, публикация фрагмента кода поможет нам диагностировать вашу проблему.   -  person    schedule 26.03.2010
comment
размещение ссылки на оригинальную проблему jpeg также может быть полезно   -  person SteelBytes    schedule 26.03.2010


Ответы (4)


Одной из наиболее частых причин левого изображения является неправильно выровненный буфер.

Я считаю, что Windows ожидает выровненный по DWORD буфер.

Одна проблема, которую я вижу в приведенном выше коде, заключается в том, что вы не хотите использовать winRowSize для копирования фактических пикселей, вы хотите использовать переменную с (шириной изображения * байтов на пиксель). winRowSize копирует размер, выровненный по DWORD, который, вероятно, слишком велик (хотя некоторые изображения могут работать, поскольку по умолчанию они выравниваются по DWORD).

Измените цикл for:

        for(i = 0; i < (width of the image * bytes per pixel); i++){ 
           rawImage[location++] = row_pointer[i]; 
        }

(Возможно, вам также придется настроить код rgb на bgr.)

person Community    schedule 26.03.2010
comment
Вы опередили меня на 1 минуту, но забыли упомянуть, что выравнивание по DWORD необходимо для каждой строки, а не только для начала буфера. - person Windows programmer; 26.03.2010
comment
Спасибо. Я исправил конвертер RGB/BGR. - person zmbush; 26.03.2010
comment
Вместо того, чтобы преобразовывать все изображение из rgb в bgr, попробуйте сделать это для каждой строки сканирования и снова используйте (ширина изображения * байты на пиксель). Как только это будет сделано, если он по-прежнему не работает, попробуйте опубликовать измененный код. - person ; 26.03.2010

Похоже, вы берете входные данные, предназначенные для RGB, и обрабатываете их как RGBA (32-битное растровое изображение вместо 24-битного).

person Jerry Coffin    schedule 26.03.2010

Возможно, вы забыли дополнить каждую строку пикселей, чтобы она занимала целое число DWORD.

person Windows programmer    schedule 26.03.2010
comment
Я это сделал. Это помогло сдвинуть каждую строку, но не проблему с цветом. - person zmbush; 26.03.2010

возможно, проблема в том, что jpeg не в rgb, а в cmyk (или даже в оттенках серого). не все jpeg являются rgb.

PS (да, я знаю, что jpeg на самом деле не rgb, а yuv, просто пытаюсь сделать этот ответ простым)

person SteelBytes    schedule 26.03.2010