I'm getting a bytearray (32 bit or 16 bit) from a source.
If the size width is odd, the last pixel in each row needs to be dropped.
If the height is odd, the last row needs to be dropped.
If the height is negative the bitmap needs to be flipped vertically.
Here is my code so far:
m_pbmiLast = new BITMAPINFO(*m_pbmi);
m_pbmiLast->bmiHeader.biWidth = abs(m_pbmiLast->bmiHeader.biWidth) - (abs(m_pbmiLast->bmiHeader.biWidth) % 2);
m_pbmiLast->bmiHeader.biHeight = abs(m_pbmiLast->bmiHeader.biHeight) - (abs(m_pbmiLast->bmiHeader.biHeight) % 2);
int biWidth = m_pbmiLast->bmiHeader.biWidth;
int biHeight = m_pbmiLast->bmiHeader.biHeight;
int iAdjustedStride = ((((biWidth * m_pbmiLast->bmiHeader.biBitCount) + 31) & ~31) >> 3);
int iRealStride = ((((m_pbmi->bmiHeader.biWidth * m_pbmi->bmiHeader.biBitCount) + 31) & ~31) >> 3);
if (m_pbmi->bmiHeader.biHeight < 0) {
/* Copy the actual data */
int iLineOffsetSource = 0;
int iLineOffsetDest = (biHeight - 1) * iRealStride;
for (int i = 0; i < biHeight; ++i) {
memcpy(&pData[iLineOffsetDest], &m_inputBuffer[iLineOffsetSource], iAdjustedStride);
iLineOffsetSource += iRealStride;
iLineOffsetDest -= iRealStride;
}
} else {
int iLineOffset = 0;
for (int i = 0; i < biHeight; ++i) {
memcpy(&pData[iLineOffset], &m_inputBuffer[iLineOffset], iAdjustedStride);
iLineOffset += iRealStride;
}
}
It doesn't flip the bitmap, and when the bitmap is an odd width, it slants the bitmap.
Can be done like so.. I include the reading and writing just to make it an SSCCE. It has little to no error.
As for my comment about new BITMAPINFO. I was saying that you don't have to allocate such a small structure on the HEAP. Ditch the new part. The only allocation you need for a bitmap is the pixels. The header and other info does not need an allocation at all.
See the Flip function below.
#include <iostream>
#include <fstream>
#include <cstring>
#include <windows.h>
typedef struct
{
BITMAPFILEHEADER Header;
BITMAPINFO Info;
unsigned char* Pixels;
} BITMAPDATA;
void LoadBmp(const char* path, BITMAPDATA* Data)
{
std::ifstream hFile(path, std::ios::in | std::ios::binary);
if(hFile.is_open())
{
hFile.read((char*)&Data->Header, sizeof(Data->Header));
hFile.read((char*)&Data->Info, sizeof(Data->Info));
hFile.seekg(Data->Header.bfOffBits, std::ios::beg);
Data->Pixels = new unsigned char[Data->Info.bmiHeader.biSizeImage];
hFile.read((char*)Data->Pixels, Data->Info.bmiHeader.biSizeImage);
hFile.close();
}
}
void SaveBmp(const char* path, BITMAPDATA* Data)
{
std::ofstream hFile(path, std::ios::out | std::ios::binary);
if (hFile.is_open())
{
hFile.write((char*)&Data->Header, sizeof(Data->Header));
hFile.write((char*)&Data->Info, sizeof(Data->Info));
hFile.seekp(Data->Header.bfOffBits, std::ios::beg);
hFile.write((char*)Data->Pixels, Data->Info.bmiHeader.biSizeImage);
hFile.close();
}
}
void Flip(BITMAPDATA* Data)
{
unsigned short bpp = Data->Info.bmiHeader.biBitCount;
unsigned int width = std::abs(Data->Info.bmiHeader.biWidth);
unsigned int height = std::abs(Data->Info.bmiHeader.biHeight);
unsigned char* out = new unsigned char[Data->Info.bmiHeader.biSizeImage];
unsigned long chunk = (bpp > 24 ? width * 4 : width * 3 + width % 4);
unsigned char* dst = out;
unsigned char* src = Data->Pixels + chunk * (height - 1);
while(src != Data->Pixels)
{
std::memcpy(dst, src, chunk);
dst += chunk;
src -= chunk;
}
std::memcpy(dst, src, chunk); //for 24-bit.
std::swap(Data->Pixels, out);
delete[] out;
}
int main()
{
BITMAPDATA Data;
LoadBmp("C:/Users/Brandon/Desktop/Bar.bmp", &Data);
Flip(&Data);
SaveBmp("C:/Users/Brandon/Desktop/Foo.bmp", &Data);
delete[] Data.Pixels;
return 0;
}
Related
I'm trying to apply the u-law algorithm to a wav file file.wav, and then create a new file file2.wav.
file.wav has 16 bits/sample, and I want to obtain a file2.wav that has 8 bits/sample.
This is my code:
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
using std::string;
using std::fstream;
typedef struct WAV_HEADER {
char RIFF[4];
unsigned long ChunkSize;
char WAVE[4];
char fmt[4];
unsigned long Subchunk1Size;
unsigned short AudioFormat;
unsigned short NumOfChan;
unsigned long SamplesPerSec;
unsigned long bytesPerSec;
unsigned short blockAlign;
unsigned short bitsPerSample;
char Subchunk2ID[4];
unsigned long Subchunk2Size;
} wav_hdr;
int headerSize = 0;
string path = "file.wav";
wav_hdr wavHeader;
FILE* openFile() {
const char* filePath;
FILE *wavFile;
headerSize = sizeof(wav_hdr);
filePath = path.c_str();
wavFile = fopen(filePath, "rb");
if (wavFile == NULL) {
printf("Error\n");
}
fread(&wavHeader, headerSize, 1, wavFile);
return wavFile;
}
int8_t MuLaw_Encode(int16_t number)
{
const uint16_t MULAW_MAX = 0x1FFF;
const uint16_t MULAW_BIAS = 33;
uint16_t mask = 0x1000;
uint8_t sign = 0;
uint8_t position = 12;
uint8_t lsb = 0;
if (number < 0)
{
number = -number;
sign = 0x80;
}
number += MULAW_BIAS;
if (number > MULAW_MAX)
{
number = MULAW_MAX;
}
for (; ((number & mask) != mask && position >= 5); mask >>= 1, position--)
;
lsb = (number >> (position - 4)) & 0x0f;
return (~(sign | ((position - 5) << 4) | lsb));
}
int fileSize(FILE *file) {
int fileSize = 0;
fseek(file, 0, SEEK_END);
fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
return fileSize;
}
double bitsPerSample() {
double bitsPerE;
bitsPerE = wavHeader.bitsPerSample;
return bitsPerE;
}
int main() {
FILE *wavFile;
wavFile = openFile();
FILE* fptr2;
fptr2 = fopen("file2.wav", "wb");
int samples_count = fileSize(wavFile) / bitsPerSample();
short int *value = new short int[samples_count];
for (int16_t i = 0; i < samples_count; i++)
{
fread(&value[i], samples_count, 1, wavFile);
cout << value[i] << " "; // the output is in the attached picture
MuLaw_Encode(value[i]);
}
fwrite(value, sizeof(char), samples_count, fptr2);
return 0;
}
I took the u-law algorithm from here (2.1. µ-Law Compression (Encoding) Algorithm)
Am I doing something wrong? Because I obtain a corrupt file.
No header is ever written to the result file, so the first part of the data would get interpreted as a header, and it would be wrong. You can see in the file that it does not start with RIFFþR�WAVEfmt or something sufficiently similar.
The data written to the result file is value, the original data read from the input file, not the µ-law encoded data (which is only cout'ed and not saved).
The loop that reads the samples reads some wrong samples, because the computation of samples_count puts the current position back at the start, where the header is.
I have code that are to paint a BMP image on a TPaintBox on a VCL form in a C++ application.
Everything works fine as long as I only have one image to paint, on one form. When I create a second form, I get sporadic access violations.
The code is called from a thread and I'm using the Synchronize function in order to synchronize with the main VCL thread as this
void TCameraForm::loadImage(FramePtr frame)
{
syncing s;
s.aFrame = frame;
s.theForm = this;
//Synchronize with UI thread
TThread::Synchronize(0, &s.fn);
}
In the code, a FramePtr is a shared pointer to one individual 'frame', holding a device dependent bitmap.
The syncing variable is a structure, holding the code for the actual painting:
//This is a trick to use VCL's TThread::Synchronize function "with parameters"
//Thanks to Mr. R. Lebeau for sharing this trick.
struct syncing
{
FramePtr aFrame;
TCameraForm* theForm;
int tag;
void __fastcall fn()
{
try
{
//Create a device dependent bitmap
BitMap aBitMap(aFrame);
//Get the bitmap memory into a TMemoryStream
TMemoryStream* ms = new TMemoryStream();
int bytes = ms->Write(aBitmap.getBuffer()->mMemoryBuffer, aBitmap.getBuffer()->mBufferSize);
ms->Position = 0;
//Create a TPicture object that will be used for drawing on the paintbox
TBitmap* tbm = new TBitmap();
tbm->LoadFromStream(ms);
TRect stretchedRect(getStretchedDimensions(tbm->Width, tbm->Height, theForm->PaintBox1->Width, theForm->PaintBox1->Height));
theForm->PaintBox1->Canvas->StretchDraw(stretchedRect, tbm);
delete ms;
delete tbm;
}
catch(...)
{
Log(lError) << "Exception occured in the CameraFrame sync function";
}
}
};
The debugger mainly stops on creation of the bitmap.
I'm using BCC builder 10.3.2 and the classic compiler.
The bitmap class looks like this:
Header
class BitMap
{
public:
BitMap(FramePtr aFrame);
BitMap(unsigned long width, unsigned long height, ColorCode c, ImageMemoryBuffer& buf);
ImageMemoryBuffer* getBuffer();
~BitMap();
bool write(const string& file);
protected:
unsigned int mWidth;
unsigned int mHeight;
ColorCode mColorCode;
ImageMemoryBuffer mImageMemoryBuffer;
bool create();
bool release();
};
And CPP:
enum { THREE_CHANNEL = 0xC,};
enum { BMP_HEADER_SIZE = 54, };
enum { ALIGNMENT_SIZE = 4, };
namespace ai
{
BitMap::BitMap(FramePtr aFrame)
:
mWidth(0),
mHeight(0),
mColorCode(ColorCodeMono8),
mImageMemoryBuffer()
{
aFrame->GetImageSize(mImageMemoryBuffer.mBufferSize);
aFrame->GetWidth(mWidth);
aFrame->GetHeight(mHeight);
VmbPixelFormatType ePixelFormat = VmbPixelFormatMono8;
aFrame->GetPixelFormat(ePixelFormat);
if((ePixelFormat != VmbPixelFormatMono8) && (ePixelFormat != VmbPixelFormatRgb8))
{
throw(MVRException("Invalid pixel format: " + toString(ePixelFormat)));
}
mColorCode = (ePixelFormat == VmbPixelFormatRgb8) ? ColorCodeRGB24 : ColorCodeMono8;
VmbUchar_t *pImage = NULL;
if (aFrame->GetImage(pImage) != VmbErrorSuccess)
{
throw(MVRException("Failed \"getting\" image"));
}
mImageMemoryBuffer.mMemoryBuffer = (unsigned char*) pImage;
if(!create())
{
Log(lError) << "There was an error creating the bitmap";
throw(MVRException("Failed creating Bitmap"));
}
}
BitMap::BitMap(unsigned long width, unsigned long height, ColorCode c, ImageMemoryBuffer& buf)
:
mWidth(width),
mHeight(height),
mColorCode(c),
mImageMemoryBuffer(buf)
{
if(!create())
{
Log(lError) << "There was an error creating the bitmap";
throw(MVRException("Failed creating bitmap"));
}
}
BitMap::~BitMap()
{
if(!release())
{
Log(lError) << "There was an error releasing the bitmap";
}
}
ImageMemoryBuffer* BitMap::getBuffer()
{
return &mImageMemoryBuffer;
}
bool BitMap::create()
{
try
{
unsigned char nNumColors; // Number of colors of our image
unsigned char nPadLength; // The padding we need to align the bitmap ALIGNMENT_SIZE
unsigned long nPaletteSize = 0; // The size of the bitmap's palette
unsigned long nHeaderSize; // The size of the bitmap's header
unsigned long nFileSize; // The size of the bitmap file
unsigned char* pBitmapBuffer; // A buffer we use for creating the bitmap
unsigned char* pCurBitmapBuf; // A cursor to move over "pBitmapBuffer"
unsigned char* pCurSrc; // A cursor to move over the given buffer "pBuffer"
unsigned long px; // A single pixel for storing transformed color information
unsigned long x; // The horizontal position within our image
unsigned long y; // The vertical position within our image
unsigned long i; // Counter for some iteration
// The bitmap header
char fileHeader[14] = { 'B','M', // Default
0,0,0,0, // File size
0,0,0,0, // Reserved
0,0,0,0 }; // Offset to image content
char infoHeader[40] = { 40,0,0,0, // Size of info header
0,0,0,0, // Width
0,0,0,0, // Height
1,0, // Default
0, 0 }; // bpp
if ( 0 == mImageMemoryBuffer.mBufferSize || 0 == mWidth || 0 == mHeight )
{
Log(lError) << "Zero bitmap buffer, width ot height in Bitmap constructor";
return false;
}
if ( mColorCode == (mColorCode & THREE_CHANNEL) )
{
nNumColors = 3;
}
else
{
nNumColors = 1;
}
// Bitmap padding always is a multiple of four Bytes. If data is not we need to pad with zeros.
nPadLength = (mWidth * nNumColors) % ALIGNMENT_SIZE;
if ( 0 != nPadLength )
{
nPadLength = ALIGNMENT_SIZE - nPadLength;
}
if ( ColorCodeRGB24 != mColorCode )
{
nPaletteSize = 256;
}
nHeaderSize = BMP_HEADER_SIZE + nPaletteSize * 4;
pBitmapBuffer = (unsigned char*)malloc( nHeaderSize + mImageMemoryBuffer.mBufferSize + (nPadLength * mHeight) );
nFileSize = nHeaderSize + mImageMemoryBuffer.mBufferSize + (nPadLength * mHeight);
// File size
fileHeader[ 2] = (char)(nFileSize);
fileHeader[ 3] = (char)(nFileSize >> 8);
fileHeader[ 4] = (char)(nFileSize >> 16);
fileHeader[ 5] = (char)(nFileSize >> 24);
// Offset to image content
fileHeader[10] = (char)(nHeaderSize);
fileHeader[11] = (char)(nHeaderSize >> 8);
fileHeader[12] = (char)(nHeaderSize >> 16);
fileHeader[13] = (char)(nHeaderSize >> 24);
// Width
infoHeader[ 4] = (char)(mWidth);
infoHeader[ 5] = (char)(mWidth >> 8);
infoHeader[ 6] = (char)(mWidth >> 16);
infoHeader[ 7] = (char)(mWidth >> 24);
// Height (has to be negative for a top down image)
infoHeader[ 8] = (char)(-(long)mHeight);
infoHeader[ 9] = (char)(-(long)mHeight >> 8);
infoHeader[10] = (char)(-(long)mHeight >> 16);
infoHeader[11] = (char)(-(long)mHeight >> 24);
// bpp
infoHeader[14] = 8 * nNumColors;
// Image size
infoHeader[20] = (char)(mImageMemoryBuffer.mBufferSize);
infoHeader[21] = (char)(mImageMemoryBuffer.mBufferSize >> 8);
infoHeader[22] = (char)(mImageMemoryBuffer.mBufferSize >> 16);
infoHeader[23] = (char)(mImageMemoryBuffer.mBufferSize >> 24);
// Palette size
infoHeader[32] = (char)(nPaletteSize);
infoHeader[33] = (char)(nPaletteSize >> 8);
infoHeader[34] = (char)(nPaletteSize >> 16);
infoHeader[35] = (char)(nPaletteSize >> 24);
// Used colors
infoHeader[36] = (char)(nPaletteSize);
infoHeader[37] = (char)(nPaletteSize >> 8);
infoHeader[38] = (char)(nPaletteSize >> 16);
infoHeader[39] = (char)(nPaletteSize >> 24);
// Write header
pCurBitmapBuf = pBitmapBuffer;
memcpy(pCurBitmapBuf, fileHeader, 14);
pCurBitmapBuf += 14;
memcpy(pCurBitmapBuf, infoHeader, 40);
pCurBitmapBuf += 40;
for(i = 0; i < nPaletteSize; ++i)
{
pCurBitmapBuf[0] = (char)(i);
pCurBitmapBuf[1] = (char)(i);
pCurBitmapBuf[2] = (char)(i);
pCurBitmapBuf[3] = 0;
pCurBitmapBuf += 4;
}
// RGB -> BGR (a Windows bitmap is BGR)
if(mColorCode == ColorCodeRGB24)
{
pCurSrc = (unsigned char*) mImageMemoryBuffer.mMemoryBuffer;
for(y=0; y < mHeight; ++y, pCurBitmapBuf += nPadLength )
{
for (x = 0; x < mWidth; ++x, pCurSrc += 3, pCurBitmapBuf += 3)
{
px = 0;
// Create a 4 Byte structure to store ARGB (we don't use A)
px = px | (pCurSrc[0] << 16) | (pCurSrc[1] << 8) | pCurSrc[2];
// Due to endianess ARGB is stored as BGRA
// and we only have to write the first three Bytes
memcpy( pCurBitmapBuf, &px, 3 );
}
// Add padding at the end of each row
memset( pCurBitmapBuf, 0, nPadLength );
}
mColorCode = ColorCodeBGR24;
}
// Mono8
else
{
if(nPadLength == 0)
{
memcpy( pCurBitmapBuf, mImageMemoryBuffer.mMemoryBuffer, mImageMemoryBuffer.mBufferSize );
}
else
{
pCurSrc = (unsigned char*)mImageMemoryBuffer.mMemoryBuffer;
for (y=0; y < mHeight; ++y, pCurSrc += mWidth * nNumColors)
{
// Write a single row of colored pixels
memcpy( pCurBitmapBuf, pCurSrc, mWidth * nNumColors );
pCurBitmapBuf += mWidth * nNumColors;
// Write padding pixels
memset(pCurBitmapBuf, 0, nPadLength);
pCurBitmapBuf += nPadLength;
}
}
}
mImageMemoryBuffer.mMemoryBuffer = pBitmapBuffer;
mImageMemoryBuffer.mBufferSize = nFileSize;
return true;
}
catch(...)
{
Log(lError) << "Exception in creation of bitmap create function";
return false;
}
}
bool BitMap::release()
{
try
{
if (mImageMemoryBuffer.mMemoryBuffer != NULL && mImageMemoryBuffer.mBufferSize > 0)
{
free(mImageMemoryBuffer.mMemoryBuffer);
mImageMemoryBuffer.mMemoryBuffer = NULL;
}
return true;
}
catch(...)
{
return false;
}
}
bool BitMap::write(const string& fName)
{
if (mImageMemoryBuffer.mMemoryBuffer == NULL)
{
return false;
}
FILE *file = fopen(fName.c_str(), "wb");
if(!file)
{
Log(lError) << "Failed opening file: " << fName;
return false;
}
fwrite(mImageMemoryBuffer.mMemoryBuffer, 1, mImageMemoryBuffer.mBufferSize, file );
fclose(file);
return true;
}
}
UPDATE: The above code works fine when executed by one thread. The problem occurs when several threads are involved. I have located the problem happening when the memcpy function is called in the creation of the bitmap (not the TBitmap), supporting this. So main problem is that the same memory is being manipulated by two or more threads at the same time.
For the above code, where would it be appropriate to incorporate a Mutex, in order to prevent the memory corruption? Or could one use another technique?
I have a exercise. It says, that the C program should be able to read the information of a bitmap file and after that it should display the picture on console.
I have already written a code but when it does not work correctly.
When I debugged the code it looks like the heap is corrupted. I thinks I have a known glitch/mistake in ScanPixelline function.
I don't know how to fix it. Can someone help me to check it?
I am relatively new to C programming.
#include "stdafx.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include "stdint.h"
#include "windows.h"
#pragma pack(1)
struct BMP
{
char Type[2]; //File type. Set to "BM".
int32_t Size; //Size in BYTES of the file.
int16_t Reserved1; //Reserved. Set to zero.
int16_t Reserved2; //Reserved. Set to zero.
int32_t OffSet; //Offset to the data.
int32_t headsize; //Size of rest of header. Set to 40.
int32_t Width; //Width of bitmap in pixels.
int32_t Height; // Height of bitmap in pixels.
int16_t Planes; //Number of Planes. Set to 1.
int16_t BitsPerPixel; //Number of Bits per pixels.
int32_t Compression; //Compression. Usually set to 0.
int32_t SizeImage; //Size in bytes of the bitmap.
int32_t XPixelsPreMeter; //Horizontal pixels per meter.
int32_t YPixelsPreMeter; //Vertical pixels per meter.
int32_t ColorsUsed; //Number of colors used.
int32_t ColorsImportant; //Number of "important" colors.
};
struct Color
{
unsigned char B;
unsigned char G;
unsigned char R;
};
struct ColorTable
{
Color *colors;
unsigned long length;
};
struct PixelArray
{
Color **pixels;
unsigned long rowCount;
unsigned long columnCount;
};
void readBMP(char *File_Name, BMP &a)
{
FILE *p = fopen(File_Name, "rb");
if (p == NULL)
{
printf("Can't open file!");
fclose(p);
return;
}
else
{
fread(&a, sizeof(BMP), 1, p);
}
fclose(p);
}
void Get_Inf(BMP a)
{
if (a.Type[0] != 'B' || a.Type[1] != 'M')
{
printf("This is not a BMP file");
}
else
{
printf("This is a BMP file\n");
printf("The size of this file is %lu bytes\n", a.Size);
printf("The witdth of this image is %lu pixels\n", a.Width);
printf("The height of this image is %lu pixels\n", a.Height);
printf("The number of bits per pixels in this image is %u\n", a.BitsPerPixel);
}
}
void scanBmpPixelLine(Color *&line, unsigned long length)
{
FILE *pointer_ = fopen("test.bmp", "rb");
line = new Color[length];
fread(line, sizeof(Color), sizeof(Color)*length, pointer_);
fclose(pointer_);
//file.read((char *)line, length * sizeof(Color));
}
void skipBmpPadding(char count)
{
FILE *pointer__ = fopen("test.bmp", "rb");
if (count == 0)
{
fclose(pointer__);
return;
}
char padding[3];
fread(&padding, sizeof(char), count, pointer__);
fclose(pointer__);
//file.read((char *)&padding, count);
}
void ReadPixelArray(BMP a, PixelArray &data)
{
FILE *pointer = fopen("test.bmp", "rb");
data.rowCount = a.Height;
data.columnCount = a.Width;
data.pixels = new Color*[data.rowCount];
char paddingCount = (4 - (a.Width * (a.BitsPerPixel / 8) % 4)) % 4;
fseek(pointer, 54, SEEK_SET);
for (int i = 0; i < data.rowCount; i++)
{
scanBmpPixelLine(data.pixels[data.rowCount - i - 1], a.Width);
skipBmpPadding(paddingCount);
}
}
void drawBmp(BMP a, PixelArray data)
{
HWND console = GetConsoleWindow();
HDC hdc = GetDC(console);
for (int i = 0; i < a.Height; i++)
for (int j = 0; j < a.Width; j++)
{
Color pixel = data.pixels[i][j];
SetPixel(hdc, j, i, RGB(pixel.R, pixel.G, pixel.B));
}
ReleaseDC(console, hdc);
}
void releaseBmpPixelArray(PixelArray data)
{
for (int i = 0; i < data.rowCount; i++)
delete[]data.pixels[i];
delete[]data.pixels;
}
int main()
{
char file_name[] = "test.bmp";
BMP a;
PixelArray data;
readBMP(file_name, a);
Get_Inf(a);
ReadPixelArray(a, data);
drawBmp(a, data);
releaseBmpPixelArray(data);
}
This function:
void scanBmpPixelLine(Color *&line, unsigned long length)
{
FILE *pointer_ = fopen("test.bmp", "rb");
line = new Color[length];
fread(line, sizeof(Color), sizeof(Color)*length, pointer_);
fclose(pointer_);
//file.read((char *)line, length * sizeof(Color));
}
For starters, the intent of the function appears to be to read one line of pixel data from the file. But instead, it's re-opening the file and reading from the beginning (where the header bytes are). I'm not sure if you are aware of that...
But the crash is a result of this line:
fread(line, sizeof(Color), sizeof(Color)*length, pointer_);
The second parameter, sizeof(Color), is the size of each element. The third parameter is the number of elements to read. The total bytes read from the file will be the multiplication of the second parameter by the third parameter. So you've redundantly multiplied by sizeof(Color) one too many times. The result is that it will overwrite the line buffer.
To fix, it should be:
fread(line, sizeof(Color), length, pointer_);
You probably want to pass the FILE* pointer obtained from your ReadPixelArray function into this function instead of re-opening the file for every line.
Another code review comment. You should just read the entire file into memory instead of redundantly opening and closing the file for each operation. Then parse the header and set a pointer to the first "line" after the header.
So I am having a problem figuring out exactly what is going wrong with trying to read any 24bpp bitmap image and re-create it in the same folder. It works with one image, but not two others that I have tested it with. When reading from the bitmap, I use the information found in the header itself. It could be said I have three questions. 1) Am I reading from bitmap correctly? 2) Am I calculating/using/writing the padding correctly? 3) Am I outputting correctly?.
Third is confirmed no with this image and its output.
Also the reason for allocating an 2d array for the Images is so that I can latter try to rotate bitmaps by 90 degrees.
Unfortunately I cannot post images, the image taken is from here, the rgb_24bpp.bmp
http://pyglet.googlecode.com/svn/trunk/tests/image/
Here is the code used for reading from the image and to calculate the padding.
ifstream myBitmap("rgb_24bpp.bmp", ios::binary | ios::beg);
// Get the total file size in bytes, testing file access
begin = myBitmap.tellg();
myBitmap.seekg(0, ios::end);
end = myBitmap.tellg();
// Actually reading image file
myBitmap.seekg( 0, ios::beg);
myBitmap.read((char*)FileHeader, sizeof(BITMAPFILEHEADER));
myBitmap.read((char*)InfoHeader, sizeof(BITMAPINFOHEADER));
test = myBitmap.tellg();
RGBQUAD ** Image = new RGBQUAD*[InfoHeader->biWidth];
for (int i = 0; i < InfoHeader->biWidth; ++i) {
Image[i] = new RGBQUAD[InfoHeader->biHeight];
}
int pitch = InfoHeader->biWidth * 3;
if (pitch % 4 != 0)
{
pitch += 4 - (pitch % 4);
}
int padding = pitch - (InfoHeader->biWidth * 3);
cout << "padding: " << padding << endl;
myBitmap.seekg(FileHeader->bfOffBits, ios::beg);
for (int i = InfoHeader->biHeight; i > 0; --i) {
for (int j = 0; j < InfoHeader->biWidth; ++j) {
myBitmap.read((char*)&Image[j][i], sizeof(RGBQUAD));
}
if (padding != 0) myBitmap.read(PadBuffer, padding);
}
myBitmap.close();
begin/end/test are all of streampos and printed on console for debugging.
And this is the code used to output/recreate the image.
ofstream BitmapOut("Output.bmp");
BitmapOut.write((char*)FileHeader, sizeof(BITMAPFILEHEADER));
BitmapOut.write((char*)InfoHeader, sizeof(BITMAPINFOHEADER));
for (int i = InfoHeader->biHeight; i > 0; --i) {
for (int j = 0; j < InfoHeader->biWidth; ++j) {
BitmapOut.write((char*)&Image[j][i], sizeof(RGBQUAD));
}
if (padding != 0) BitmapOut.write("\0\0\0\0\0\0\0", padding);
}
BitmapOut.close();
I have confirmed that both headers are indeed correct and can pull data from them properly in 3 different tests.
Utilizing this guys code (sorry, this project is non-commercial and self-study only).
reading a .bmp file in c++
With the exception of commenting out the reserved in the RGBQUAD and making effectively a RGBTRI instead.
You can do it like this.. Also, if you don't want to make a temporary array to copy the pixels, you can easily read, seek, read, seek, etc.. OR you can just read all at once. There are so many ways to read a bitmap and be efficient/inefficient. It's up to you how you want to do it. Another efficient way to do it is to SAVE the BitmapInfoHeader and BitmapFileHeader. Then when you decide to write the bitmap to the disk, just write them headers first then the pixels. WAY faster and easier.. I did NOT do that in this example. I'll leave that up to you to figure out.
Here is a sample code I wrote for answering your question. I prefer to use 1-dimensional arrays.
#include <fstream>
#include <cstring>
#include <windows.h>
typedef struct
{
unsigned int width, height;
unsigned char* pixels;
} Bitmap;
void InitBitmap(Bitmap* bmp)
{
if (bmp)
{
bmp->width = 0;
bmp->height = 0;
bmp->pixels = NULL;
}
}
void FreeBitmap(Bitmap* bmp)
{
if (bmp && bmp->pixels)
{
bmp->width = 0;
bmp->height = 0;
delete[] bmp->pixels;
bmp->pixels = NULL;
}
}
bool ReadBitmap(const char* FilePath, Bitmap* bmp)
{
std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
if (!bmp || !hFile.is_open())
return false;
BITMAPINFO Info;
BITMAPFILEHEADER Header;
memset(&Info, 0, sizeof(Info));
memset(&Header, 0, sizeof(Header));
hFile.read((char*)&Header, sizeof(Header));
hFile.read((char*)&Info.bmiHeader, sizeof(Info.bmiHeader));
bmp->width = Info.bmiHeader.biWidth;
bmp->height = Info.bmiHeader.biHeight < 0 ? -Info.bmiHeader.biHeight : Info.bmiHeader.biHeight;
size_t size = Info.bmiHeader.biSizeImage;
bmp->pixels = new unsigned char[size];
hFile.seekg(Header.bfOffBits, std::ios::beg);
hFile.read((char*)bmp->pixels, size);
hFile.close();
return true;
}
bool WriteBitmap(const char* FilePath, Bitmap* bmp)
{
std::fstream hFile(FilePath, std::ios::out | std::ios::binary);
if (!bmp || !hFile)
return false;
BITMAPINFO Info;
BITMAPFILEHEADER Header;
memset(&Info, 0, sizeof(Info));
memset(&Header, 0, sizeof(Header));
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biWidth = bmp->width;
Info.bmiHeader.biHeight = bmp->height;
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = 24;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biSizeImage = 0;
Header.bfType = 0x4D42;
Header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
size_t size = (((24 * bmp->width + 31) & ~31) / 8) * bmp->height;
hFile.write((char*)&Header, sizeof(Header));
hFile.write((char*)&Info.bmiHeader, sizeof(Info.bmiHeader));
hFile.write((char*)bmp->pixels, size);
hFile.close();
return true;
}
int main()
{
Bitmap bmp;
InitBitmap(&bmp);
ReadBitmap("C:/Users/Brandon/Desktop/foo.bmp", &bmp);
WriteBitmap("C:/Users/Brandon/Desktop/foo2.bmp", &bmp);
FreeBitmap(&bmp);
}
The instructions for libjpeg-turbo here describes the TurboJPEG API thus: "This API wraps libjpeg-turbo and provides an easy-to-use interface for compressing and decompressing JPEG images in memory". Great, but are there some solid examples of using this API available? Just looking to decompress a fairly vanilla jpeg in memory.
I've found a few bits such as https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c, which appears to be using the TurboJPEG API, but are there any more solid/varied examples?
The source for libjpeg-turbo is well documented, so that does help.
Ok, I know that you did already solve your problem, but as some people, just like me, could be searching some simple example I will share what I created.
It is an example, compressing and decompressing an RGB image. Otherwise I think that the API documentation of TurboJPEG is quite easy to understand!
Compression:
#include <turbojpeg.h>
const int JPEG_QUALITY = 75;
const int COLOR_COMPONENTS = 3;
int _width = 1920;
int _height = 1080;
long unsigned int _jpegSize = 0;
unsigned char* _compressedImage = NULL; //!< Memory is allocated by tjCompress2 if _jpegSize == 0
unsigned char buffer[_width*_height*COLOR_COMPONENTS]; //!< Contains the uncompressed image
tjhandle _jpegCompressor = tjInitCompress();
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &_jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
tjDestroy(_jpegCompressor);
//to free the memory allocated by TurboJPEG (either by tjAlloc(),
//or by the Compress/Decompress) after you are done working on it:
tjFree(&_compressedImage);
After that you have the compressed image in _compressedImage.
To decompress you have to do the following:
Decompression:
#include <turbojpeg.h>
long unsigned int _jpegSize; //!< _jpegSize from above
unsigned char* _compressedImage; //!< _compressedImage from above
int jpegSubsamp, width, height;
unsigned char buffer[width*height*COLOR_COMPONENTS]; //!< will contain the decompressed image
tjhandle _jpegDecompressor = tjInitDecompress();
tjDecompressHeader2(_jpegDecompressor, _compressedImage, _jpegSize, &width, &height, &jpegSubsamp);
tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, buffer, width, 0/*pitch*/, height, TJPF_RGB, TJFLAG_FASTDCT);
tjDestroy(_jpegDecompressor);
Some random thoughts:
I just came back over this as I am writing my bachelor thesis, and I noticed that if you run the compression in a loop it is preferable to store the biggest size of the JPEG buffer to not have to allocate a new one every turn. Basically, instead of doing:
long unsigned int _jpegSize = 0;
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &_jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
we would add an object variable, holding the size of the allocated memory long unsigned int _jpegBufferSize = 0; and before every compression round we would set the jpegSize back to that value:
long unsigned int jpegSize = _jpegBufferSize;
tjCompress2(_jpegCompressor, buffer, _width, 0, _height, TJPF_RGB,
&_compressedImage, &jpegSize, TJSAMP_444, JPEG_QUALITY,
TJFLAG_FASTDCT);
_jpegBufferSize = _jpegBufferSize >= jpegSize? _jpegBufferSize : jpegSize;
after the compression one would compare the memory size with the actual jpegSize and set it to the jpegSize if it is higher than the previous memory size.
I ended up using below code as a working example for both JPEG encoding and decoding. Best example that I can find, it's self-contained that initializes a dummy image and output the encoded image to a local file.
Below code is NOT my own, credit goes to https://sourceforge.net/p/libjpeg-turbo/discussion/1086868/thread/e402d36f/#8722 . Posting it here again to help anyone finds it's difficult to get libjpeg turbo working.
#include "turbojpeg.h"
#include <iostream>
#include <string.h>
#include <errno.h>
using namespace std;
int main(void)
{
unsigned char *srcBuf; //passed in as a param containing pixel data in RGB pixel interleaved format
tjhandle handle = tjInitCompress();
if(handle == NULL)
{
const char *err = (const char *) tjGetErrorStr();
cerr << "TJ Error: " << err << " UNABLE TO INIT TJ Compressor Object\n";
return -1;
}
int jpegQual =92;
int width = 128;
int height = 128;
int nbands = 3;
int flags = 0;
unsigned char* jpegBuf = NULL;
int pitch = width * nbands;
int pixelFormat = TJPF_GRAY;
int jpegSubsamp = TJSAMP_GRAY;
if(nbands == 3)
{
pixelFormat = TJPF_RGB;
jpegSubsamp = TJSAMP_411;
}
unsigned long jpegSize = 0;
srcBuf = new unsigned char[width * height * nbands];
for(int j = 0; j < height; j++)
{
for(int i = 0; i < width; i++)
{
srcBuf[(j * width + i) * nbands + 0] = (i) % 256;
srcBuf[(j * width + i) * nbands + 1] = (j) % 256;
srcBuf[(j * width + i) * nbands + 2] = (j + i) % 256;
}
}
int tj_stat = tjCompress2( handle, srcBuf, width, pitch, height,
pixelFormat, &(jpegBuf), &jpegSize, jpegSubsamp, jpegQual, flags);
if(tj_stat != 0)
{
const char *err = (const char *) tjGetErrorStr();
cerr << "TurboJPEG Error: " << err << " UNABLE TO COMPRESS JPEG IMAGE\n";
tjDestroy(handle);
handle = NULL;
return -1;
}
FILE *file = fopen("out.jpg", "wb");
if (!file) {
cerr << "Could not open JPEG file: " << strerror(errno);
return -1;
}
if (fwrite(jpegBuf, jpegSize, 1, file) < 1) {
cerr << "Could not write JPEG file: " << strerror(errno);
return -1;
}
fclose(file);
//write out the compress date to the image file
//cleanup
int tjstat = tjDestroy(handle); //should deallocate data buffer
handle = 0;
}
In the end I used a combination of random code found on the internet (e.g. https://github.com/erlyvideo/jpeg/blob/master/c_src/jpeg.c) and the .c and header files for libjeg-turbo, which are well documented.
This official API is a good information source aswell.
Here's a fragment of code what I use to load jpeg's from memory. Maybe it will require a bit of fixing, because I extracted it from different files in my project. It will load both - grayscale and rgb images (bpp will be set either to 1 or to 3).
struct Image
{
int bpp;
int width;
int height;
unsigned char* data;
};
struct jerror_mgr
{
jpeg_error_mgr base;
jmp_buf jmp;
};
METHODDEF(void) jerror_exit(j_common_ptr jinfo)
{
jerror_mgr* err = (jerror_mgr*)jinfo->err;
longjmp(err->jmp, 1);
}
METHODDEF(void) joutput_message(j_common_ptr)
{
}
bool Image_LoadJpeg(Image* image, unsigned char* img_data, unsigned int img_size)
{
jpeg_decompress_struct jinfo;
jerror_mgr jerr;
jinfo.err = jpeg_std_error(&jerr.base);
jerr.base.error_exit = jerror_exit;
jerr.base.output_message = joutput_message;
jpeg_create_decompress(&jinfo);
image->data = NULL;
if (setjmp(jerr.jmp)) goto bail;
jpeg_mem_src(&jinfo, img_data, img_size);
if (jpeg_read_header(&jinfo, TRUE) != JPEG_HEADER_OK) goto bail;
jinfo.dct_method = JDCT_FLOAT; // change this to JDCT_ISLOW on Android/iOS
if (!jpeg_start_decompress(&jinfo)) goto bail;
if (jinfo.num_components != 1 && jinfo.num_components != 3) goto bail;
image->data = new (std::nothrow) unsigned char [jinfo.output_width * jinfo.output_height * jinfo.output_components];
if (!image->data) goto bail;
{
JSAMPROW ptr = image->data;
while (jinfo.output_scanline < jinfo.output_height)
{
if (jpeg_read_scanlines(&jinfo, &ptr, 1) != 1) goto bail;
ptr += jinfo.output_width * jinfo.output_components;
}
}
if (!jpeg_finish_decompress(&jinfo)) goto bail;
image->bpp = jinfo.output_components;
image->width = jinfo.output_width;
image->height = jinfo.output_height;
jpeg_destroy_decompress(&jinfo);
return true;
bail:
jpeg_destroy_decompress(&jinfo);
if (image->data) delete [] data;
return false;
}