"The word 'radical' derives from the Latin word for root. Therefore, if you want to get to the root of anything you must be radical. It is no accident that the word has now been totally demonized..." -- Gore Vidal
This article is intended to be a primer for novices who wish to learn about bitmap file structure and to perform basic image processing. It is not a how-to article for graphics. The reason for this - C++ has no native graphics capabilities. You either have to use GDI (under Windows), OpenGL, SDL, to name a few API's. The code can be used as is anyway, so you can use it to solve some of the puzzles here.
The bitmap class outlined in this article is complete enough to compile, and was adapted from my own class wrapper around OpenGL and SDL. The project got leggy, and code was executing very slow for what I wanted, so I scrapped the project and kept the classes for reuse.
Bitwise Color Operations
In my previous article, I mentioned how color information can be stored in a long integer. This integer has a special name under Windows, called a COLORREF data type. The color data is ordered into fields that look something like 0xAABBGGRR, with RR (red data) occupying the LSB side. Well, in order to go from discrete RGBA values to that format and back again, you will need to understand how bit masking and arithmetic shifts work. Let's tackle these now.
In digital logic, there are a handful of basic operations:
AND, OR, NOT, NAND, NOR, X0R, XNOR
These have "analogs" (har, har) in C++. I strongly encourage you to go to the library and learn about these digital operations. Studying boolean algebra can become very useful. What you need to know for this article is that bits can be ANDded and ORed together to get at the data. In a nutshell, the AND operation yeilds a binary 1 only if both input bits are 1. So we can use ANDing to get at the fields of interest in our long int. If you need to brush up on your hex, there are several articles that can help.
Extracting Fields from a COLORREF
CODE :
int a = 0x0000ff00; // decimal 65,280
int b = 0x0000fa19; // decimal 64,025
int c = 0;
// suppose we are interested in in getting at the 'fa' part of b,
// we must use a bitwise and, which is a binary operator
c = a & b; // a is the 'mask' being applied to b
// c should now contain the hex value 0x0000fa00, so we need to shift
// the bits eight bit positions to the right (LSB direction), and we
// can combine these operations in one shot using parentheses
c = (a & b) >> 8; // c now has the value 0x000000fa
Constructing COLORREF Values from Discrete RGBA Values
CODE :
int r = 0x0000001a
int g = 0x000000fc
int b = 0x000000cd
int a = 0x000000ff
COLORREF c = 0;
// lets make a single COLORREF with these values
c = r + (g << 8) + (b << 16) + (a << 24);
// now c = 0xffcdfc1a...simple
Bitmap Class Definition and Code
I have included all the necessary files here for you to begin screwing around with bitmaps. It can only open existing bitmaps and alter them, but you should be able to do that by examining the code and header structure. I have included a stub (main.cpp) to outline how to use the class. I strongly encourage you to read up on bitmap file structure, though a knowledge of it is not necessary to use my class.
bitmap_class.h
CODE :
#ifndef BITMAP_CLASS_H
#define BITMAP_CLASS_H
#include <stdlib.h>
#include <windows.h>
#include "udt.h"
using namespace std;
/*+----------------------------------------------------------+
| cBitmap |
+----------------------------------------------------------+
| |
| Purpose: |
| The cBitmap class makes it easy to load and use bitmaps.|
| |
+----------------------------------------------------------+*/
// attempt to open the file using binary access
fin.open(filename, ios::binary);
if(fin.is_open())
{
// read in the file info
fin.read((char *)(&m_header), sizeof(BITMAPFILEHEADER));
fin.read((char *)(&m_info), sizeof(BITMAPINFOHEADER));
// create an array that can take the pixel data
m_pixel_data = new BYTE[m_info.biSizeImage];
// rgba format requires 32 bits, not 24 (hence the * 4 / 3)
m_rgba_data = new BYTE[m_info.biSizeImage * 4 / 3];
// read the pixels
fin.read((char *)(m_pixel_data), m_info.biSizeImage);
// close the file
fin.close();
// now, the hard part - we need to get the BGR data to RGBA
BYTE * src(m_pixel_data);
BYTE * dst(m_rgba_data);
// now we walk through the arrays and perform the operation
for(unsigned int index(0); index < m_info.biSizeImage / 3;
index++)
{
// transform BGR to RGB (OpenGL uses this format -
// you can thank me later...)
*(dst) = *(src + 2);
*(dst + 1) = *(src + 1);
*(dst + 2) = *(src);
// now check the colorkey, and set colorkey
// pixels to alpha = 0
if((*(dst) == red_color_key) &&
(*(dst + 1) == green_color_key) &&
(*(dst + 2) == blue_color_key))
{
// colorkey matches, set to transparent
*(dst + 3) = 0x00;
}
else
{
// the pixel isn't a colorkey pixel, set to opaque
*(dst + 3) = 0xff;
}
// increment the array pointers according
src += 3;
dst += 4;
}
}
else
{
// post file not found message
cout << "What is this " << filename << " you speak of?";
}
}
/*+----------------------------------------------------------+
| ~cBitmap |
+----------------------------------------------------------+*/
cBitmap::~cBitmap()
{
// deallocate arrays, if they were successfully allocated
if(m_pixel_data)
{
delete [] m_pixel_data;
}
if(m_rgba_data)
{
delete [] m_rgba_data;
}
}
/*+----------------------------------------------------------+
| GetPixel |
+----------------------------------------------------------+*/
COLORREF cBitmap::GetPixel(int x, int y)
{
// offset into the bitmap and place into a COLORREF variable
COLORREF temp_color(0);
int temp_red(0), temp_green(0), temp_blue(0), temp_alpha(0);
int temp_x(x), temp_y(m_info.biHeight - 1 - y);
// we can put the individual colors in the right format
temp_color += temp_alpha << 24;
temp_color += temp_blue << 16;
temp_color += temp_green << 8;
temp_color += temp_red;
return temp_color;
}
/*+----------------------------------------------------------+
| PutPixel |
+----------------------------------------------------------+*/
void cBitmap::PutPixel(int x, int y, BYTE red, BYTE green, BYTE blue, BYTE alpha)
{
// offset into the bitmap and insert rgba vals
int temp_red(0), temp_green(0), temp_blue(0), temp_alpha(0);
int temp_x(x), temp_y(m_info.biHeight - 1 - y);
if(fout.is_open())
{
// same as before, only outputting now
fout.write((char *)(&m_header), sizeof(BITMAPFILEHEADER));
fout.write((char *)(&m_info), sizeof(BITMAPINFOHEADER));
// read off the color data in the bass ackwards MS way
for(unsigned int index(0); index < number_of_bytes; index += 4)
{
red = m_rgba_data[index];
green = m_rgba_data[index + 1];
blue = m_rgba_data[index + 2];
// get a color in Windows format
color = test.GetPixel(0, 0);
cout << "Obtained a pixel value..." << endl;
// RGB data analysis
cout << "At the point (0, 0) the color is: ";
cout << "(" << int(Red(color)) << ", ";
cout << int(Green(color)) << ", " << int(Blue(color)) << ")" << endl;
// change a color "magenta at (0, 0)"
test.PutPixel(0, 0, 255, 0, 255, 0);
cout << "Placed a new pixel color at (0, 0)." << endl;
// save the bitmap
test.SaveBitmap("newfile.bmp");
cout << "Saved the file to this directory as newfile.bmp." << endl;
// destructor fired and deallocated memory
cout << "Look in the top left corner of newfile.bmp!" << endl;
return 0;
}
// function to obtain red data from a COLORREF value
BYTE Red(COLORREF color)
{
return (color & 0x000000ff);
}
// and green
BYTE Green(COLORREF color)
{
return (color & 0x0000ff00) << 8;
}
// and also blue
BYTE Blue(COLORREF color)
{
return (color & 0x00ff0000) << 16;
}
// and alpha if you're froggy
BYTE Alpha(COLORREF color)
{
return (color & 0xff000000) << 24;
}
Conclusion
I have actually used this class to solve some of the challenges on this website. I encourage you to keep screwing around with stuff, because you learn more by doing for yourself than any book can teach you! Always keep pushing the envelope.
- 195948557
P.S.: You will see me often say that you should go to the library. I'm not trying to be condescending - this is the secret to knowledge and ability. Yes, they may be analog devices, but books are so very valuable. There is nothing wrong with educating yourself. Some of the greatest knowledge you could possibly obtain is that which no one else can teach you.
Educate yourself.
Become strong.
Guard your secrets.
<3<3<3
- 195948557
Cast your vote on this article 10 - Highest, 1 - Lowest
Comments: Published: 9 comments.
HackThisSite is the collective work of the HackThisSite staff, licensed under a CC BY-NC license.
We ask that you inform us upon sharing or distributing.