I have been working on this problem for a while now.
I am trying to add JPEG support to a program with libjpeg.
For the most part, it is working fairly well, But for some JPEGs, they show up like the picture on the left.
(Compare with the original image.)
It may not be obvious, but the background shows up with alternating red green and blue rows. If anyone has seen this behavior before and knows a probable cause, I would appreciate any input.
I have padded the rows to be multiples of four bytes, and it only slightly helped the issue.
Code:
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);
Solution: I changed the RGB/BGR converter to this:
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;
}
}
}
And it worked like a charm. Thanks to roygbiv.
One of the most common causes of the left image is a buffer that is not properly aligned.
I believe Windows expects a DWORD aligned buffer.
One issue I see with the above code is that you do not want to use winRowSize to copy the actual pixels, you want to use a variable with the (width of the image * bytes per pixel). winRowSize copies the DWORD aligned size which is probably too big (although some images may work as they fall on DWORD alignment by default.)
Change the for loop:
for(i = 0; i < (width of the image * bytes per pixel); i++){
rawImage[location++] = row_pointer[i];
}
(You may also have to adjust the rgb to bgr code.)