Search code examples
c++algorithmdata-structuresbitmaparray-algorithms

C++ Best way to read BitMap "Right Way up" into 1D Vector


I am trying to read a BitMap "The Right Way Up" into a 1D Vector. Here is my first attempt. It is pretty clunky:

void BitMap::ReadBMP(const char* filename)
{
    FILE* f = fopen(filename, "rb");
if(f == NULL)
    throw "Argument Exception";

unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header

// extract image height and width from header
m_width = *(int*)&info[18];
m_height = *(int*)&info[22];

cout << endl;
cout << "  Name: " << filename << endl;
cout << " Width: " << m_width << endl;
cout << "Height: " << m_height << endl;

int row_padded = (m_width*3 + 3) & (~3);
unsigned char* data = new unsigned char[row_padded];
unsigned char tmp;

std::deque<Element> mydeque;
std::vector<Element> bmpRow;

for(int i = 0; i < m_height; i++)
{
    fread(data, sizeof(unsigned char), row_padded, f);
    for(int j = 0; j < m_width*3; j += 3)
    {
                    // BGRA format
        Element element;
        element.Elements[0] = data[j+2];
        element.Elements[1] = data[j+1];
        element.Elements[2] = data[j];
        element.Elements[3] = 0; // for alpha
        bmpRow.push_back(element);
    }
    mydeque.insert (mydeque.begin(),bmpRow.begin(),bmpRow.end());
    bmpRow.clear();
}

std::copy(mydeque.begin(), mydeque.end(), std::back_inserter(m_pixelVec)); 
cout<< "After Deque Copy" << endl;

fclose(f);
delete data;

}

Problem is, I need to iterate through the data at a different point of the APP like this - note h and W swapped for setPixel. Please ignore this part, this is just to demonstrate the BitMap needs to be rotated in C++ part above:

for (int h = 0; h<imageHeight; h++)
{
    for (int w = 0; w<imageWidth; w++)
    {
        int p = 0;
        Pixel pixel = currentImagePixelVec.get(pixelVecLoc);
        p = (pixel.Alpha<<24) | (pixel.Red<<16) | (pixel.Green<<8) | pixel.Blue;                
        imageData.setPixel(w, h, p);            
        pixelVecLoc++;
    }
}

So I would like to rotate the BitMap as I read it into the 1D Vector. Can you suggest a good way?

EDIT: This is really just for testing purposes. I am only using BitMaps I know the height and width of etc.. I would like to know from an algorithm point of view how people would do it


Solution

  • As far as I understand, you want to transpose the image data; that is, store columns of pixels in contiguous segments of m_pixelVec, rather than rows.

    It seems to me that the easiest way to do this is by placing values into a 2D data structure as they are read, then flattening it. That makes it trivial to change column-major vs row-major order, by just flipping the indices.

    To access any point when I want, without any push_backs, I preallocated the whole thing, which assumes the Element type is default-constructible. I used a vector of vectors, but a 2D array could work just as well.

    I noticed that you put the first row read (i == 0) at the end of your vector and assumed you want to keep that behavior. Hence the index m_height-i-1 when assigning to bmpcolrow. Another possibility is to just use bmpcolrow[j][i] and then reverse the columns when inserting (use col.rbegin() and col.rend() in the last line).

    std::vector<std::vector<Element>> bmpcolrow(m_width, std::vector<Element>(m_height));
    
    for(int i = 0; i < m_height; i++) {
        fread(data, sizeof(unsigned char), row_padded, f);
        for(int j = 0; j < m_width; j++) {
                    // BGRA format
            Element element;
            element.Elements[0] = data[3*j+2];
            element.Elements[1] = data[3*j+1];
            element.Elements[2] = data[3*j];
            element.Elements[3] = 0; // for alpha
            bmpcolrow[j][m_height-i-1] = element;
        }
    }
    
    for (const auto& col : bmpcolrow)
        m_pixelVec.insert(m_pixelVec.end(), col.begin(), col.end());