Why is there an extra letter appearing with screen buffers - c++

So I tried making a basic game for testing movement with console. I decided to use screen buffers, but when I compile and run the c++ code the letter a appears with the player (character #)
Here's the code:
#include "pch.h"
#include <iostream>
#include <stdio.h>
#include <Windows.h>
int width = 50, height = 20;
int main()
{
wchar_t *screen = new wchar_t[width * height];
HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
SetConsoleActiveScreenBuffer(hConsole);
DWORD bytesWritten = 0;
int x = width / 2, y = height / 2;
while (true) {
screen[width * height - 1] = '\0';
for (int i = 0; i < width * height; i++) screen[i] = L' ';
if (GetAsyncKeyState('W') & 1) y--;
else if (GetAsyncKeyState('S') & 1) y++;
if (GetAsyncKeyState('A') & 1) x--;
else if (GetAsyncKeyState('D') & 1) x++;
// keep the character inside
if (x < 0) x = 0;
else if (x > width-1) x = width-1;
if (y < 0) y = 0;
else if (y > height - 1) y = height - 1;
wsprintf(&screen[width * y + x], L"#");
WriteConsoleOutputCharacter(hConsole, screen, width * height, { 0,0 }, &bytesWritten);
}
return 0;
}
Running it looks like this:
I use visual studio 2017.
Why does the letter a appear?
Thanks in advance!

Related

Creating a Tetris Game with field problem

I'm starting with programming C++ and I was trying to create a Tetris game. I've added assets and defined the field size. Before adding Game Logic, I've noticed that my field wasn't "ok". It should be a table and not three small tables. I'm not sure what is the problem, maybe it's in the //draw field. Can you help me?
Code
#include <string>
#include "Windows.h"
using namespace std;
int nScreenWidth = 80; // Console Screen Size X (columns)
int nScreenHeight = 30; // Console Screen Size Y (rows)
wstring tetro[7];
int nFieldW = 12;
int nFieldH = 18;
unsigned char* pField = nullptr;
int rotation(int ex, int ey, int r) {
switch (r % 4) {
case 0: return ey * 4 + ex; // 0 graus
case 1: return 12 + ey - (ex * 4); // 90 graus
case 2: return 15 - (ey * 4) - ex; // 180 graus
case 3: return 3 - ey + (ex * 4); // 270 graus
}
return 0;
}
int main()
{
//create assets
tetro[0].append(L"..X.");
tetro[0].append(L"..X.");
tetro[0].append(L"..X.");
tetro[0].append(L"..X.");
tetro[1].append(L"..X.");
tetro[1].append(L".XX.");
tetro[1].append(L".X..");
tetro[1].append(L"....");
tetro[3].append(L"....");
tetro[3].append(L".XX.");
tetro[3].append(L".XX.");
tetro[3].append(L"....");
tetro[4].append(L"..X.");
tetro[4].append(L".XX.");
tetro[4].append(L".X..");
tetro[4].append(L"....");
tetro[5].append(L"....");
tetro[5].append(L".XX.");
tetro[5].append(L"..X.");
tetro[5].append(L"..X.");
tetro[6].append(L"....");
tetro[6].append(L".XX.");
tetro[6].append(L".X..");
tetro[6].append(L".X..");
pField = new unsigned char[nFieldW*nFieldH];
for (int x = 0; x < nFieldW; x++) //Board Boundary
for (int y = 0; y < nFieldH; y++)
pField[y*nFieldW + x] = (x == 0 || x == nFieldW - 1 || y == nFieldH - 1) ? 9 : 0;
// Create Screen Buffer
wchar_t* screen = new wchar_t[nScreenWidth * nScreenHeight];
for (int i = 0; i < nScreenWidth * nScreenHeight; i++) screen[i] = L' ';
HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
SetConsoleActiveScreenBuffer(hConsole);
DWORD dwBytesWritten = 0;
bool bGameOver = false;
while (!bGameOver) {
//draw field
for (int x = 0; x < nFieldW; x++)
for (int y = 0; y < nFieldH; y++)
screen[(y + 2)*nScreenWidth + (x + 2)] = L" ABCDEFG=#"[pField[y*nFieldW + x]];
//display frame
WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth * nScreenHeight, { 0,0 }, &dwBytesWritten);
}
}
Solution
Thank you in advance!
You write out your screen contents as one big string. The console will display this on one line, only wrapping to the next line when it reaches the right side of the console buffer.
You need to either set the console window and buffer widths to the same width as your internal screen buffer (80 characters), or (preferably) write each line individually to the console.

RGBA pixel data into D3DLOCKED_RECT

I'm trying to update a 128x128 D3DLOCKED_RECT with sub images using the following code, but it seems to squish them down along the top, the X offset is ignored and the y offset is 60 percent off.
I've also tried to make the texture the correct size and copy it into a 128x128 texture at the correct location using RECT, however this is very slow and didn't seem to work correctly when I attempted it. There must be way to do it using the raw pixel data?
Any help would be much appreciated :)
EDIT: I got it semi working using the below code, the locations are now correct and the sizes. But it's only using the blue channel and everything is grey scale (blue scale?)
srcdata = (byte *) pixels;
dstdata = (unsigned int *)lockrect.pBits;
for (y = yoffset; y < (yoffset + height); y++)
{
for (x = xoffset; x < (xoffset + width); x++)
{
dstdata[ ( y * lockrect.Pitch / dstbytes + x ) + 0] = (unsigned int)srcdata[0];
dstdata[ ( y * lockrect.Pitch / dstbytes + x ) + 1] = (unsigned int)srcdata[1];
dstdata[ ( y * lockrect.Pitch / dstbytes + x ) + 2] = (unsigned int)srcdata[0];
dstdata[ ( y * lockrect.Pitch / dstbytes + x ) + 3] = (unsigned int)srcdata[3];
srcdata += srcbytes;
}
}'
END Edit
Test call after creating the 128x128 texture:
int x, y;
byte temp[132*132*4];
// Test texture (pink and black checker)
for( y = 0; y < 16; y++ )
{
for( x = 0; x < 16; x++ )
{
if(( y < 8 ) ^ ( x < 8 ))
((uint *)&temp)[y*16+x] = 0xFFFF00FF;
else ((uint *)&temp)[y*16+x] = 0xFF000000;
}
}
UpdateSubImage (0, 0, 16, 16, temp )
The update Fuction:
void UpdateSubImage (int xoffset, int yoffset, int width, int height, const
GLvoid *pixels)
{
int x, y;
int srcbytes = 4; //Hard coded for now, as all tests are RGBA
int dstbytes = 4; // ^
byte *srcdata;
byte *dstdata;
D3DLOCKED_RECT lockrect;
pTexture->LockRect( 0, &lockrect, NULL, 0);
srcdata = (byte *) pixels;
dstdata = (byte *) lockrect.pBits;
dstdata += (yoffset * width + xoffset) * dstbytes;
for (y = yoffset; y < (yoffset + height); y++)
{
for (x = xoffset; x < (xoffset + width); x++)
{
if (srcbytes == 1)
{
if (dstbytes == 1)
dstdata[0] = srcdata[0];
else if (dstbytes == 4)
{
dstdata[0] = srcdata[0];
dstdata[1] = srcdata[0];
dstdata[2] = srcdata[0];
dstdata[3] = srcdata[0];
}
}
else if (srcbytes == 3)
{
if (dstbytes == 1)
dstdata[0] = ((int) srcdata[0] + (int) srcdata[1] + (int) srcdata[2]) / 3;
else if (dstbytes == 4)
{
dstdata[0] = srcdata[2];
dstdata[1] = srcdata[1];
dstdata[2] = srcdata[0];
dstdata[3] = 255;
}
}
else if (srcbytes == 4)
{
if (dstbytes == 1)
dstdata[0] = ((int) srcdata[0] + (int) srcdata[1] + (int) srcdata[2]) / 3;
else if (dstbytes == 4)
{
dstdata[0] = srcdata[2];
dstdata[1] = srcdata[1];
dstdata[2] = srcdata[0];
dstdata[3] = srcdata[3];
}
}
// advance
srcdata += srcbytes;
dstdata += dstbytes;
}
}
pTexture->UnlockRect(0);
}
What the output looks like:
What the output should look like:
You're assuming that the data accessable through lockrect.pBits is linear in memory. This is in general not the case. Instead you have a constant offset between your rows which is defined by the lockrect.Pitch value.
To get the address of a pixel in the destination use:
byte * destAddr = (lockrect.pBits + y * lockrect.Pitch + 4 * x);
// for 32 bit images. For other formats adjust the hard-coded 4.
Thanks for the help :), in the end the following code worked:
Can it be made faster?
for (y = yoffset; y < (yoffset + height); y++)
{
for (x = xoffset; x < (xoffset + width); x++)
{
ARGB pixel;
pixel.r = srcdata[0];
pixel.g = srcdata[1];
pixel.b = srcdata[2];
pixel.a = srcdata[3];
memcpy( &dstdata[lockrect.Pitch * y + dstbytes * x], &pixel, dstbytes );
srcdata += srcbytes;
}
}

Why is console animation so slow on Windows? (And is there a way to improve speed?)

Well I was bored so wanted to make an animation in a console window.
Now when I setup the first bits I noticed it is very slow, something around 333ms for a whole screen to fill with characters..
I am wondering if there is a way to at least get ~20 fps?
Here is my code:
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include <array>
#define WIDTH (100)
#define HEIGHT (35)
bool SetWindow(int Width, int Height) {
_COORD coord;
coord.X = Width; coord.Y = Height;
_SMALL_RECT Rect;
Rect.Left = 0; Rect.Top = 0;
Rect.Bottom = Height - 1; Rect.Right = Width - 1;
HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (Handle == NULL)return FALSE;
SetConsoleScreenBufferSize(Handle, coord);
if(!SetConsoleWindowInfo(Handle, TRUE, &Rect)) return FALSE;
return TRUE;
}
std::array<std::array<unsigned char, WIDTH+1>, HEIGHT> Screen;//WIDTH+1 = prevent cout from undefined behaviour
void Putchars(unsigned char x){
for(int row = 0; row < HEIGHT; ++row){
std::fill(Screen[row].begin(),Screen[row].end(),x);
Screen[row].at(WIDTH) = 0;//here = prevent cout from undefined behaviour
}
}
void ShowFrame(DWORD delay = 0,bool fPutchars = false, unsigned char x = 0){
if(fPutchars)Putchars(x);
if(delay)Sleep(delay);
system("CLS");
for(int row = 0; row < HEIGHT; ++row)
std::cout << Screen[row].data() << std::flush;
}
int _tmain(int argc, _TCHAR* argv[]){//sould execute #~63 fps, yet it executes #~3-4 fps
if(SetWindow(100,HEIGHT)){
for(unsigned char i = 219; i != 0; --i)
ShowFrame(16,true, i);
}
return 0;
}
Edit: after reading numerous answers, tips and comments I finally worked it out, thank you guys, this is my final "base" code:
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <iostream>
#include <array>
#define WIDTH (100)
#define HEIGHT (34)
HANDLE current;
HANDLE buffer;
bool SetWindow(int Width, int Height) {
_COORD coord;
coord.X = Width; coord.Y = Height;
_SMALL_RECT Rect;
Rect.Left = 0; Rect.Top = 0;
Rect.Bottom = Height - 1; Rect.Right = Width - 1;
HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (Handle == NULL)return FALSE;
SetConsoleScreenBufferSize(Handle, coord);
if(!SetConsoleWindowInfo(Handle, TRUE, &Rect)) return FALSE;
return TRUE;
}
std::array<std::array<CHAR, WIDTH+1>, HEIGHT> Screen;//WIDTH+1 = prevent cout from undefined behaviour
void Putchars(CHAR x){
for(int row = 0; row < HEIGHT; ++row){
std::fill(Screen[row].begin(),Screen[row].end(),x);
Screen[row].at(WIDTH) = 0;//here = prevent cout from undefined behaviour
}
}
void ShowFrame(DWORD delay = 0, bool fPutchars = false, CHAR x = 0){
if(fPutchars)Putchars(x);
if(delay)Sleep(delay);
//system("CLS");
_COORD coord;
coord.X = 0;
for(int row = 0; row < HEIGHT; ++row)
{
coord.Y = row;
FillConsoleOutputCharacterA(buffer,Screen[row].data()[0],100,coord,NULL);
}
}
int _tmain(int argc, _TCHAR* argv[]){//sould execute #~63 fps, yet it executes #~3-4 fps
SetWindow(WIDTH, HEIGHT);
current = GetStdHandle (STD_OUTPUT_HANDLE);
buffer = CreateConsoleScreenBuffer (
GENERIC_WRITE,
0,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
SetConsoleActiveScreenBuffer (buffer);
if(SetWindow(WIDTH,HEIGHT)){
for(CHAR i = 219; i != 0; --i)
ShowFrame(250,true, i);
}
CloseHandle (buffer); //clean up
return 0;
}
and it seems to work very fast :)
Just glancing at your code, you're spawning a separate program ("CLS") once per frame. That's horrendously slow all by itself.
Contrary to some of the comments, the Windows console is capable of at least fairly reasonable speed if used even close to correctly (as in: you can update the console faster than any monitor can display the data).
Just for reference, here's a version of John Conway's Game of Life written for the Windows console. For timing purposes, it just generates a random starting screen and runs for 2000 generations, then stops. On my machine, it does 2000 generations in about 2 seconds, or around 1000 frames per second (useless, since a typical monitor can only update at around 60-120 Hz). Under 32-bit Windows with a full-screen console, it can roughly double that (again, at least on my machine). I'm pretty sure with a little work, this could be sped up some more, but I've never seen any reason to bother.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <io.h>
#define ROWS 50
#define COLS 80
// The total number of generations is really double this number.
int generations = 1000;
int civ1[ROWS+2][COLS+2], civ2[ROWS+2][COLS+2];
CHAR_INFO disp[ROWS][COLS];
HANDLE console;
COORD size = { COLS, ROWS };
COORD src = { 0, 0};
SMALL_RECT dest = { 0, 0, COLS, ROWS };
void ClrScrn(char attrib) {
COORD pos = { 0, 0};
DWORD written;
unsigned size;
size = ROWS * COLS;
FillConsoleOutputCharacter(console, ' ', size, pos, &written);
FillConsoleOutputAttribute(console, attrib, size, pos, &written);
SetConsoleCursorPosition(console, pos);
}
void fill_edges(int civ1[ROWS+2][COLS+2]) {
int i, j;
for (i=1; i<=ROWS; ++i) {
civ1[i][0] = civ1[i][COLS];
civ1[i][COLS+1] = civ1[i][1];
}
for (j=1; j<=COLS; ++j) {
civ1[0][j] = civ1[ROWS][j];
civ1[ROWS+1][j] = civ1[1][j];
}
civ1[0][0] = civ1[ROWS][COLS];
civ1[ROWS+1][COLS+1] = civ1[1][1];
civ1[0][COLS+1] = civ1[ROWS][1];
civ1[ROWS+1][0] = civ1[1][COLS];
}
void update_generation(int old_gen[ROWS+2][COLS+2],
int new_gen[ROWS+2][COLS+2])
{
int i, j, count;
for (i = 1; i <= ROWS; ++i)
{
for (j = 1; j <= COLS; ++j)
{
count = old_gen[i - 1][j - 1] +
old_gen[i - 1][j] +
old_gen[i - 1][j + 1] +
old_gen[i][j - 1] +
old_gen[i][j + 1] +
old_gen[i + 1][j - 1] +
old_gen[i + 1][j] +
old_gen[i + 1][j + 1];
switch(count)
{
case 2:
new_gen[i][j] = old_gen[i][j];
break;
case 3:
new_gen[i][j] = 1;
disp[i-1][j-1].Char.AsciiChar = '*';
break;
default:
new_gen[i][j] = 0;
disp[i-1][j-1].Char.AsciiChar = ' ';
break;
}
}
}
WriteConsoleOutput(console, (CHAR_INFO *)disp, size, src, &dest);
fill_edges(new_gen);
}
void initialize(void)
{
int i, j;
ClrScrn(0x71);
srand(((unsigned int)time(NULL))|1);
for (i = 1; i <= ROWS; ++i)
{
for (j = 1; j <= COLS; ++j)
{
civ1[i][j] = (int)(((__int64)rand()*2)/RAND_MAX);
disp[i-1][j-1].Char.AsciiChar = civ1[i][j] ? '*' : ' ';
disp[i-1][j-1].Attributes = 0x71;
}
}
WriteConsoleOutput(console, (CHAR_INFO *)disp, size, src, &dest);
fill_edges(civ1);
}
int main(int argc, char **argv) {
int i;
if ( argc != 1)
generations = atoi(argv[1]);
console = GetStdHandle(STD_OUTPUT_HANDLE);
initialize();
for (i = 0; i <generations; ++i)
{
update_generation(civ1, civ2);
update_generation(civ2, civ1);
}
return EXIT_SUCCESS;
}
If nothing else, this has a ClrScrn function you may find handy.
Do not use cout to write to console. Use the WriteConsole from console API instead. And you can use double buffering like in normal graphical animations with SetConsoleActiveScreenBuffer.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682073.aspx
Also don't use system ("cls") to clear the screen, FillConsoleOutputCharacter is a lot faster.

Memory leak whilst freeing a 2d array

I am creating a version of Conway's Game of Life. It is eventually going to be run on an Arduino and will control LEDs so the memory footprint is important. It seems that I have a memory leak, I believe that this leak occurs whilst frreing a two dimensional array. If anyone could help me with this then I would be very grateful.
Thanks,
Joe
VLD's output is:
c:\projects\gameoflifecpp\gameoflifecpp\gameoflifecpp.cpp (72): GameOfLifeCPP.exe!GenerateGrid + 0xA bytes
c:\projects\gameoflifecpp\gameoflifecpp\gameoflifecpp.cpp (185): GameOfLifeCPP.exe!ProcessGrid + 0x7 bytes
c:\projects\gameoflifecpp\gameoflifecpp\gameoflifecpp.cpp (46): GameOfLifeCPP.exe!wmain + 0x9 bytes
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (552): GameOfLifeCPP.exe!__tmainCRTStartup + 0x19 bytes
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (371): GameOfLifeCPP.exe!wmainCRTStartup
0x7C817077 (File and line number not available): kernel32.dll!RegisterWaitForInputIdle + 0x49 bytes
Code is:
// GameOfLifeCPP.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <vld.h>
#define WIDTH 75
#define HEIGHT 88
#define GENERATION_COUNT_LIMIT -1
long _generationCount = 0;
// These get set by controls on the table
long _delay = 1000;
bool _run = true;
bool _trail = true;
bool _randomize = false;
char* _colours = "roy";
int _tmain(int argc, _TCHAR* argv[])
{
system("pause");
short** grid = GenerateGrid(false);
short** trailGrid = GenerateGrid(true); // This is used to record all prev cells
while(_run)
{
if (_randomize)
{
grid = GenerateGrid(false);
trailGrid = GenerateGrid(true);
// Fade out LEDs
// Clear the historical grids that we compare
_randomize = false;
_generationCount = 0;
}
OutputGrid(grid, trailGrid);
if (_trail)
trailGrid = CalculateTrailGrid(grid, trailGrid);
short** nextGrid = ProcessGrid(grid);
// Release the old grid
for(int i = 0; i < sizeof(nextGrid); i++)
{
delete(grid[i]);
}
delete(grid);
grid = nextGrid;
// We don't want to just sleep we need to find out the start and end time
Sleep(_delay);
bool foundRecurance = false;
// Need to detect recurence, have a buffer of 5-10 prev grids and one
// hundredth ago, one thousanth etc that we compare to.
_generationCount++;
if (foundRecurance || _generationCount == GENERATION_COUNT_LIMIT)
_randomize = true;
_CrtDumpMemoryLeaks();
//system("pause");
}
return 0;
}
short** GenerateGrid(bool empty)
{
// The coordinates are y,x because it is simpler to output a row of chars
// when testing in the command line than it is to output a column of chars
short** grid = new short*[HEIGHT];
for(int y = 0; y < HEIGHT; y++)
{
short* row = new short[WIDTH];
for(int x = 0; x < WIDTH; x++)
{
// There is no point creating random numbers that we aren't going
// to use
if (empty)
row[x] = 0;
else
row[x] = rand() % 5 == 1 ? 1 : 0;
// Might want to adjust this or make it random
}
grid[y] = row;
}
return grid;
}
void OutputGrid(short** grid, short** trailGrid)
{
// This is terribly inefficent but I don't care since it is only for
// testing on my laptop
system("cls");
HANDLE hConsole;
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
for(int y = 0; y < HEIGHT; y++)
{
for(int x = 0; x < WIDTH; x++)
{
int curState = grid[y][x];
if (curState == 0 && _trail) // If it isn't alive then show the trail
curState = trailGrid[y][x];
switch (curState)
{
case 0: SetConsoleTextAttribute(hConsole, 0); break;
case 1: SetConsoleTextAttribute(hConsole, GetColour(0)); break;
case 2: SetConsoleTextAttribute(hConsole, GetColour(1)); break;
case -1: SetConsoleTextAttribute(hConsole, GetColour(2)); break;
}
//if (curState == 1 || curState == 2)
// std::cout << "*";
//else
std::cout << " ";
}
SetConsoleTextAttribute(hConsole, 15);
std::cout << std::endl;
}
}
int GetColour(int index)
{
int colour = 0;
switch(_colours[index])
{
case 'r': colour = 12; break;
case 'o': colour = 6; break;
case 'y': colour = 14; break;
}
colour = colour * 16;
return colour;
}
int ProcessCell(short** grid, int x, int y)
{
// Get the value for each of the surrounding cells
// We use the formula (x - 1 + WIDTH) % WIDTH because that means that if the
// Current cell is at 0,0 then top left is WIDTH-1,WIDTH-1 and so on.
// This makes the grid wrap around.
// We don't care if the cells value is 1 or 2 it is either live or dead
int topLeft = (
grid[(y - 1 + HEIGHT) % HEIGHT][(x - 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int top = (grid[(y - 1 + HEIGHT) % HEIGHT][x] > 0) ? 1 : 0;
int topRight =
(grid[(y - 1 + HEIGHT) % HEIGHT][(x + 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int left = (grid[y][(x - 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int self = (grid[y][x] > 0) ? 1 : 0;
int right = (grid[y][(x + 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int bottomLeft =
(grid[(y + 1 + HEIGHT) % HEIGHT][(x - 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int bottom = (grid[(y + 1 + HEIGHT) % HEIGHT][x] > 0) ? 1 : 0;
int bottomRight =
(grid[(y + 1 + HEIGHT) % HEIGHT][(x + 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
// Count up the surrounding cells to decide the current cell's state
int liveCount = topLeft + top + topRight + left +
right + bottomLeft + bottom + bottomRight;
int live = 0;
if (self > 0)
{
// Both are alive, just different colours
if (liveCount == 2)
live = 1;
if (liveCount == 3)
live = 2;
}
else if (liveCount == 3)
{
// Brought back to life, we don't care that it is the wrong
// colour - it looks better
live = 1;
}
return live;
}
short** ProcessGrid(short** grid)
{
short** nextGrid = GenerateGrid(true);
for (int y = 0; y < HEIGHT; y++)
{
for (int x = 0; x < WIDTH; x++)
{
nextGrid[y][x] = ProcessCell(grid, x, y);
}
}
return nextGrid;
}
short** CalculateTrailGrid(short** grid, short** trailGrid)
{
// Any previously live cells are marked
short** nextGrid = GenerateGrid(true);
for (int y = 0; y < HEIGHT; y++)
{
for (int x = 0; x < WIDTH; x++)
{
int state = grid[y][x];
if (state == 0)
state = trailGrid[y][x]; // Not alive currently but was
if (state != 0)
state = -1;
nextGrid[y][x] = state;
}
}
return nextGrid;
}
Just a quick 5 min cleanup in notepad... should give you some ideas... avoids any possible memory leaks...
#include "stdafx.h"
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <vld.h>
#include <vector>
#define WIDTH 75
#define HEIGHT 88
#define GENERATION_COUNT_LIMIT -1
long _generationCount = 0;
// These get set by controls on the table
long _delay = 1000;
bool _run = true;
bool _trail = true;
bool _randomize = false;
char* _colours = "roy";
typedef std::vector<std::vector<short>> grid_t; // Use std::vector
int _tmain(int argc, _TCHAR* argv[])
{
system("pause");
grid_t grid = GenerateGrid(false);
grid_t trailGrid = GenerateGrid(true); // This is used to record all prev cells
while(_run)
{
if (_randomize)
{
grid = GenerateGrid(false);
trailGrid = GenerateGrid(true);
// Fade out LEDs
// Clear the historical grids that we compare
_randomize = false;
_generationCount = 0;
}
OutputGrid(grid, trailGrid);
if (_trail)
trailGrid = CalculateTrailGrid(grid, trailGrid);
grid_t nextGrid = ProcessGrid(grid);
// Release the old grid
grid = nextGrid;
// We don't want to just sleep we need to find out the start and end time
Sleep(_delay);
bool foundRecurance = false;
// Need to detect recurence, have a buffer of 5-10 prev grids and one
// hundredth ago, one thousanth etc that we compare to.
_generationCount++;
if (foundRecurance || _generationCount == GENERATION_COUNT_LIMIT)
_randomize = true;
_CrtDumpMemoryLeaks();
//system("pause");
}
return 0;
}
grid_t GenerateGrid(bool empty)
{
// The coordinates are y,x because it is simpler to output a row of chars
// when testing in the command line than it is to output a column of chars
grid_t grid;
for(int y = 0; y < HEIGHT; y++)
{
std::vector<short> row;
for(int x = 0; x < WIDTH; x++)
row[x] = empty ? 0 : rand() % 5 == 1 ? 1 : 0;
grid.push_back(row);
}
return grid;
}
void OutputGrid(const grid_t& grid, const grid_t& trailGrid)
{
// This is terribly inefficent but I don't care since it is only for
// testing on my laptop
system("cls");
HANDLE hConsole;
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
for(int y = 0; y < HEIGHT; y++)
{
for(int x = 0; x < WIDTH; x++)
{
int curState = grid[y][x];
if (curState == 0 && _trail) // If it isn't alive then show the trail
curState = trailGrid[y][x];
switch (curState)
{
case 0: SetConsoleTextAttribute(hConsole, 0); break;
case 1: SetConsoleTextAttribute(hConsole, GetColour(0)); break;
case 2: SetConsoleTextAttribute(hConsole, GetColour(1)); break;
case -1: SetConsoleTextAttribute(hConsole, GetColour(2)); break;
}
}
SetConsoleTextAttribute(hConsole, 15);
std::cout << std::endl;
}
}
int GetColour(int index)
{
switch(_colours[index])
{
case 'r': return 16 * 12;
case 'o': return 16 * 6;
case 'y': return 16 * 14;
default: return 0;
}
}
int ProcessCell(const grid_t& grid, int x, int y)
{
// Get the value for each of the surrounding cells
// We use the formula (x - 1 + WIDTH) % WIDTH because that means that if the
// Current cell is at 0,0 then top left is WIDTH-1,WIDTH-1 and so on.
// This makes the grid wrap around.
// We don't care if the cells value is 1 or 2 it is either live or dead
int topLeft = (grid[(y - 1 + HEIGHT) % HEIGHT][(x - 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int top = (grid[(y - 1 + HEIGHT) % HEIGHT][x] > 0) ? 1 : 0;
int topRight = (grid[(y - 1 + HEIGHT) % HEIGHT][(x + 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int left = (grid[y][(x - 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int self = (grid[y][x] > 0) ? 1 : 0;
int right = (grid[y][(x + 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int bottomLeft = (grid[(y + 1 + HEIGHT) % HEIGHT][(x - 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
int bottom = (grid[(y + 1 + HEIGHT) % HEIGHT][x] > 0) ? 1 : 0;
int bottomRight = (grid[(y + 1 + HEIGHT) % HEIGHT][(x + 1 + WIDTH) % WIDTH] > 0) ? 1 : 0;
// Count up the surrounding cells to decide the current cell's state
int liveCount = topLeft + top + topRight + left + right + bottomLeft + bottom + bottomRight;
int live = 0;
if (self > 0)
{
// Both are alive, just different colours
if (liveCount == 2)
live = 1;
if (liveCount == 3)
live = 2;
}
else if (liveCount == 3)
{
// Brought back to life, we don't care that it is the wrong
// colour - it looks better
live = 1;
}
return live;
}
grid_t ProcessGrid(const grid_t& grid)
{
grid_t nextGrid = GenerateGrid(true);
for (int y = 0; y < HEIGHT; y++)
{
for (int x = 0; x < WIDTH; x++)
nextGrid[y][x] = ProcessCell(grid, x, y);
}
return nextGrid;
}
grid_t CalculateTrailGrid(const grid_t& grid, const grid_t& trailGrid)
{
// Any previously live cells are marked
grid_t nextGrid = GenerateGrid(true);
for (int y = 0; y < HEIGHT; y++)
{
for (int x = 0; x < WIDTH; x++)
nextGrid[y][x] = state == 0 ? trailGrid[y][x] : -1;
}
return nextGrid;
}

C++: Optimizing a simple image clipping operation... too many if() statements

The code I have here is preliminary. I am focusing on the clipping procedures. There seems to be a lot of if() statements, and I'm hoping someone will have a clever way of optimizing this at least a little bit.
If you're wondering what m_nStride is, it's the number of elements to add to any given pixel pointer to reach the pixel directly below it (y + 1, x + 0). But either way, that is unimportant pertaining to my question.
The general idea is to fill a rectangular area of the image (32-bpp) using coordinates left, top, width and height. In cases where the coordinates would cause that area to cross the boundaries of the image, the area would be clipped to fit inside the image instead of being interpreted as an error.
void Image::Clear(int nLeft, int nTop, int nWidth, int nHeight, DWORD dwColor)
{
if(nWidth <= 0) return;
if(nHeight <= 0) return;
if(nLeft >= m_nWidth) return;
if(nTop >= m_nHeight) return;
if(nLeft < 0)
{
nWidth += nLeft;
if(nWidth <= 0)
return;
nLeft = 0;
}
if(nTop < 0)
{
nHeight += nTop;
if(nHeight <= 0)
return;
nTop = 0;
}
if(nLeft + nWidth > m_nWidth)
{
nWidth -= ((nLeft + nWidth) - m_nWidth);
if(nWidth <= 0)
return;
}
if(nTop + nHeight > m_nHeight)
{
nHeight -= ((nTop + nHeight) - m_nHeight);
if(nHeight <= 0)
return;
}
DWORD *p = m_pBuffer + (m_nStride * nTop) + nLeft;
for(int y = 0; y < nHeight; y++)
{
for(int x = 0; x < nWidth; x++)
p[x] = dwColor;
p += m_nStride;
}
}
For performance, the if overhead is virtually zero compared to the for loops. Nonetheless, as an exercise here's a version with fewer checks. It clips the bounds first and then only needs to check width and height being positive.
void Image::Clear(int nLeft, int nTop, int nWidth, int nHeight, DWORD dwColor)
{
if(nLeft < 0)
{
nWidth += nLeft;
nLeft = 0;
}
if(nTop < 0)
{
nHeight += nTop;
nTop = 0;
}
if(nLeft + nWidth > m_nWidth)
{
nWidth = m_nWidth - nLeft;
}
if(nTop + nHeight > m_nHeight)
{
nHeight = m_nHeight - nTop;
}
if(nWidth <= 0) return;
if(nHeight <= 0) return;
DWORD *p = m_pBuffer + (m_nStride * nTop) + nLeft;
for(int y = 0; y < nHeight; y++)
{
for(int x = 0; x < nWidth; x++)
p[x] = dwColor;
p += m_nStride;
}
}