// COTD Entry submitted by Joshua Carmody [paladinoftheweb@hotmail.com]
#include <windows.h>
/*************************************************************************/
// drawingSurface class. Allows for very easy drawing to a window.
/*************************************************************************/
class drawingSurface {
public:
drawingSurface(int width, int height); // Constructor
~drawingSurface(); // Destructor
void setPixel(int x, int y, unsigned long color); // Set Pixel data in our pointer to the specified color
void drawLine(int x1, int y1, int x2, int y2, unsigned long color); // Draw a line from x1,y1 to x2,y2 in the specified color
void display(int x, int y, HWND windowHandle); // Copies window pixels from memory pointer to screen.
void erase(unsigned long color); // Completely fills the bitmap with the specified color
int getWidth(); // Return the bitmap's width.
int getHeight(); // Return the bitmap's height.
protected:
HBITMAP scratchPadBitmap; // Temporary Bitmap
unsigned long *windowContents; // Pointer to pixel data to display in the window
int windowWidth, windowHeight; // Store the window's dimensions
};
/*************************************************************************/
// drawingSurface constructor. Creates a "drawing surface" (DIB) for the
// application to draw to. Takes two parameters, width, and height.
/*************************************************************************/
drawingSurface::drawingSurface(int width, int height) {
windowWidth = width; // Store the width
windowHeight = height; // Store the height
HANDLE heap = GetProcessHeap(); // Get a memory block from windows
// Allocate memory for the BITMAPINFO and BITMAPINFOHEADER structures.
// The BITMAPINFO structure is just a BITMAPINFOHEADER structure with
// RGBQUADs at the end, so we use this code so both structures share the
// same memory.
BITMAPINFO *format = (BITMAPINFO *)HeapAlloc(heap, 0, sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) * 3));
BITMAPINFOHEADER *header = (BITMAPINFOHEADER*)format;
header->biSize = sizeof(BITMAPINFOHEADER); // This structure is as big as it is. This is a stupid value required by windows
header->biWidth = width; // Width of the bitmap
header->biHeight = -height; // Height of the bitmap. Negative values indicate a top-down bitmap instead of a bottom-up
header->biPlanes = 1; // 1 plane. Always 1 plane. Never changes.
header->biBitCount = 32; // 32 bits per pixel, TrueColor
header->biCompression = BI_RGB; // No compression
header->biSizeImage = 0; // The size of the image, we can ommit this for a non-compressed image
header->biXPelsPerMeter = 0; // number of pixels per horizontal meter (we don't care)
header->biYPelsPerMeter = 0; // number of pixels per vertical meter (we don't care)
header->biClrUsed = 0; // How many colors are used? 0 means maxiumum
header->biClrImportant = 0; // How many colors are "important", 0 means all of 'em
// Create the DIB
scratchPadBitmap = CreateDIBSection(
NULL, // DC for the DIB top be compatible with, doesn't matter for 32-bit images
format, // Bitmap Information
DIB_RGB_COLORS, // Direct RGB values, instead of paletted values
(void**)&windowContents, // A pointer to receive a pointer to the bitmap's bytes
NULL, // This parameter and the next one have to deal with file mapping,
0 // a subject I don't completely understand.
);
HeapFree(heap,0,format); // Free previously allocated memory
}
/*************************************************************************/
// Destructor for drawingSurface, cleans up vairables and frees memory.
/*************************************************************************/
drawingSurface::~drawingSurface() {
DeleteObject(scratchPadBitmap);
}
/*************************************************************************/
// setPixel, sets the pixel at x, y to color inside the DIB
/*************************************************************************/
void drawingSurface::setPixel(int x, int y, unsigned long color) {
*(windowContents + (x + (y * windowWidth))) = color;
}
/*************************************************************************/
// drawLine draws a line from x1, y1 to x2, y2 in the specified color
/*************************************************************************/
void drawingSurface::drawLine(int x1, int y1, int x2, int y2, unsigned long color) {
int i, j, k, deltaX, deltaY, a, testX, testY;
testX = deltaX = (x2 - x1);
testY = deltaY = (y2 - y1);
if(testX < 0) { testX *= -1; }
if(testY < 0) { testY *= -1; }
if(testX > testY) {
for(i=0;i<testX;i++) {
j = ((i * deltaY) / testX);
k = ((deltaX * i) / testX);
*(windowContents + ((k + x1) + ((j + y1) * windowWidth))) = color;
}
}
else if(testX <= testY) {
for(i=0;i<testY;i++) {
j = ((i * deltaX) / testY);
k = ((deltaY * i) / testY);
*(windowContents + ((j + x1) + ((k + y1) * windowWidth))) = color;
}
}
}
/*************************************************************************/
// Erases the entire bitmap (fills with the specifier color)
/*************************************************************************/
void drawingSurface::erase(unsigned long color) {
int i, j;
for(i=0;i<windowWidth;i++) {
for(j=0;j<windowHeight;j++) {
*(windowContents + i + (j * windowWidth)) = color;
}
}
}
/*************************************************************************/
// display, Render the DIB inside the specified window at the specifier
// coordinates
/*************************************************************************/
void drawingSurface::display(int x, int y, HWND windowHandle) {
HDC windowDC = GetDC(windowHandle); // Get the DC for this window
HDC memoryDC = CreateCompatibleDC(windowDC); // Create a compatible one in memory
// Save the old bitmap currently in the memory DC. We do this so we can
// put it back before we delete it. I don't know why, but windows makes
// you do this.
HBITMAP oldBitmap = (HBITMAP)SelectObject(memoryDC, scratchPadBitmap);
// Copy the bitmap data from the memory DC to the one in the window
BitBlt(
windowDC, // The DC of the destination window
x, // The X coordinate to copy to
y, // the Y coordinate to copy to
windowWidth, // Width of the area we're copying
windowHeight, // Height of the area we're copying
memoryDC, // DC in memory that holds the source bitmap
0, // X coordinate in source bitmap
0, // Y coordinate in source bitmap
SRCCOPY // COPY the data (instead of doing a bitwise AND or something)
);
SelectObject(memoryDC, oldBitmap); // Put the old bitmap back before we delete the DC
DeleteDC(memoryDC); // Delete the DC
ReleaseDC(windowHandle, windowDC); // Release the window's DC
}
/*************************************************************************/
// Return the DIB's width
/*************************************************************************/
int drawingSurface::getWidth() {
return windowWidth;
}
/*************************************************************************/
// Return the DIB's height
/*************************************************************************/
int drawingSurface::getHeight() {
return windowHeight;
}
/*************************************************************************/
// the bitmapWindow class. This uses the above drawingSurface class to
// easily create a window that we can draw to directly.
/*************************************************************************/
class bitmapWindow {
public:
bitmapWindow(int x, int y, int width, int height, HINSTANCE currentInstance); // Constructor
~bitmapWindow(); // Destructor
static int getNumberOfBitmapWindows(); // Returns static int below
void setPixel(int x, int y, unsigned long color); // Set Pixel data in our pointer to the specified color
void drawLine(int x1, int y1, int x2, int y2, unsigned long color); // Draw a line from x1,y1 to x2,y2 in the specified color
void erase(unsigned long color); // Passes the erase request to the drawingSurface
void flipIntoView(); // Copies window pixels from memory pointer to screen.
protected:
HWND windowHandle; // The handle for the window we'll pop up.
void registerWindowClass(HINSTANCE currentInstance); // For registering the class of window we'll use
static BOOL classIsRegistered; // Have we, or have we not, registered our window class?
static int numberOfBitmapWindows; // The number of these classes in use
int windowWidth, windowHeight; // Store the window's dimensions
drawingSurface *windowContents; // easy DIB class defined above
};
// Initialize static vairables
BOOL bitmapWindow::classIsRegistered = 0;
int bitmapWindow::numberOfBitmapWindows = 0;
/*************************************************************************/
// Window Handler for our bitmapWindows.
/*************************************************************************/
LRESULT CALLBACK bitmapWindowHandler(HWND windowHandle, // Handle to the window this procedure is handling
UINT message, // A message related to said window
WPARAM wparam, // A parameter vairable
LPARAM lparam) // Another
{
switch(message) {
case WM_QUIT: // Quit, if we're supposed to
case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
break;
}
// Take default action if not told to quit
return DefWindowProc(windowHandle, message, wparam, lparam);
}
/*************************************************************************/
// constructor for bitmapWindow. Takes 5 parameters. An X coordinate for
// the window to appear, a Y coordinate for the window to appear, the
// width of the window, the height of the window, and the handle to this
// program's instance.
/*************************************************************************/
bitmapWindow::bitmapWindow(int x, int y, int width, int height, HINSTANCE currentInstance) {
// We only need to register the window class once.
if(!classIsRegistered) { registerWindowClass(currentInstance); }
windowWidth = width + 7; // Store the width
windowHeight = height + 25; // Store the height
// Create a window
windowHandle = CreateWindow(
"Library_BMP_Window", // Name of the window class (registered above)
"Window", // Name of the window, appears in the window title bar
WS_BORDER | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU, // Window style
x, // X coordinate of the window on-screen
y, // Y coordinate of the window on-screen
windowWidth, // width of the window
windowHeight, // height of the window
NULL, // Handle to a parent window (none)
NULL, // Handle to a child window (none)
currentInstance, // Handle to the current Instance
0 // Pointer to extra data I don't care about
);
ShowWindow(windowHandle, SW_SHOW); // Show the window
UpdateWindow(windowHandle); // Redundant, just in case
windowContents = new drawingSurface(width, height); // Create the drawing surface, this class is defined above
numberOfBitmapWindows++; // Keep track of the number of these classes
}
/*************************************************************************/
// Destructor, cleans up allocated memory and updates the number of
// these windows
/*************************************************************************/
bitmapWindow::~bitmapWindow() {
DestroyWindow(windowHandle);
numberOfBitmapWindows--;
}
/*************************************************************************/
// getNumberOfBitmapWindows, returns: the number of bitmap windows!
/*************************************************************************/
int bitmapWindow::getNumberOfBitmapWindows() {
return numberOfBitmapWindows;
}
/*************************************************************************/
// Passes a request to change pixel data on to the drawing surface.
/*************************************************************************/
void bitmapWindow::setPixel(int x, int y, unsigned long color) {
windowContents->setPixel(x, y, color);
}
/*************************************************************************/
// Passes a request to draw a line on to the drawing surface.
/*************************************************************************/
void bitmapWindow::drawLine(int x1, int y1, int x2, int y2, unsigned long color) {
windowContents->drawLine(x1, y1, x2, y2, color);
}
/*************************************************************************/
// Passes a request to erase a line on to the drawingSurface.
/*************************************************************************/
void bitmapWindow::erase(unsigned long color) {
windowContents->erase(color);
}
/*************************************************************************/
// Display's this windows drawing surface
/*************************************************************************/
void bitmapWindow::flipIntoView() {
windowContents->display(0, 0, windowHandle);
}
/*************************************************************************/
// Registers the window class we use
/*************************************************************************/
void bitmapWindow::registerWindowClass(HINSTANCE currentInstance) {
WNDCLASS windowClass;
windowClass.style = CS_OWNDC; // The style of the window. CS_OWNDC means every window has it's own DC
windowClass.lpfnWndProc = bitmapWindowHandler; // The function to call when this window receives a message
windowClass.cbClsExtra = 0; // Extra bytes to allocate for this class (none)
windowClass.cbWndExtra = 0; // Extra bytes to allocate for each window (none)
windowClass.hInstance = currentInstance; // This application's instance
windowClass.hIcon = LoadIcon(currentInstance, IDI_APPLICATION); // A standard Icon
windowClass.hCursor = LoadCursor(currentInstance, IDC_ARROW); // A standard cursor
windowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE; // A standard background
windowClass.lpszMenuName = NULL; // No menus in this window
windowClass.lpszClassName = "Library_BMP_Window"; // A name for this class
RegisterClass(&windowClass); // Register the class
classIsRegistered = 1; // Did we register the class? Yes, we did.
}
|