I'm a newbie in sdl. this is my question: I create a text surface use sdl_ttf.dll.
like this
TTF_Font* myFont = TTF_OpenFont(szFontPath, text.m_iFontSize);
SDL_Surface *textSurface = TTF_RenderUNICODE_Blended(myFont, (const
Uint16*)text.m_wstrFilePath.c_str(), textColor);
This works fine and the textsurface is ok if I blit on my screen,which is used to play a video.
SDL_BlitSurface(textSurface , null, screen, offset);
But the problem is the text on the screen keep blinking in every 3 /4 seconds, even I set the
screen with SDL_DOUBLEBUF and SDL_HWSURFACE, it will not help. so it went another way.I decide to blit the text on the screen before it display. i use this function:
blitSurface2YUV(text, vp->bmp, &rectOffset); //vp->bmp is the sdl_overlay
display it: SDL_DisplayYUVOverlay(vp->bmp, &rcRView); //rcView is the offset to the screen
"blitSurface2YUV" is defined here:
int blitSurface2YUV(SDL_Surface *src, SDL_Overlay *dst, SDL_Rect *dstrect)
{
Uint8 r, g, b;
int y1,u1,v1;
int y,x;
int height = src->h < dstrect->h ? src->h: dstrect->h;
int width = src->w < dstrect->w ? src->w: dstrect->w;
int uv_off = 0;
Uint32 pixel;
if(dst->format != SDL_YV12_OVERLAY)
return 1;
for(y = 0; y < height; ++y)
{
for(x = 0; x < width; ++x)
{
switch(src->format->BitsPerPixel)
{
case 8:
pixel = *((Uint8*)src->pixels + y*src->pitch + x);
break;
case 16:
pixel = *((Uint16*)src->pixels + y*src->pitch/2 + x);
break;
case 32:
pixel = *((Uint32*)src->pixels + y*src->pitch/4 + x);
break;
default:
return -1;
}
SDL_GetRGB(pixel, src->format, &r, &g, &b);
rgb2yuv(r, g, b, &y1, &u1, &v1);
memset(dst->pixels[0] + (dstrect->y + y) * dst->pitches[0] + (dstrect->x + x),
(Uint8)y1, 1);
if((x%2 == 0 ) && (y%2 == 0 ))
{
memset(dst->pixels[1] + (uv_off + dstrect->y /2) * dst->pitches[1] + (dstrect->x/2 + x/2),
(Uint8)v1, 1);
memset(dst->pixels[2] + (uv_off + dstrect->y /2) * dst->pitches[2] + (dstrect->x/2 + x/2),
(Uint8)u1, 1);
}
}
if(y%2 == 0)++uv_off;
}
return 0;
}
This can solve the blink problem. but the text on the screen has a black background, which is supposed to be empty.
so can someone tell me what is the problem?
This is not really an answer but definitely too long for a comment.
I think you're trying to solve the wrong problem. Your initial problem is that your text, generated using TTF_RenderUNICODE_Blended "blinks" at an interval when calling SDL_BlitSurface on your screen surface. This is supposed to work, and has worked in all of my SDL projects.
This means that there has to be something wrong with either your rendering loop or your text, if you could post more code about this it would be helpful!
You may add this into the loop. And than black pixels won't process.
if(pixel == 0 )
continue;
Here is corrected function.
int blitSurface2YUV(SDL_Surface *src, SDL_Overlay *dst, SDL_Rect *dstrect)
{
Uint8 r, g, b;
int y1,u1,v1;
int y,x;
int height = src->h < dstrect->h ? src->h: dstrect->h;
int width = src->w < dstrect->w ? src->w: dstrect->w;
int uv_off = 0;
Uint32 pixel;
if(dst->format != SDL_YV12_OVERLAY)
return 1;
for(y = 0; y < height; ++y)
{
for(x = 0; x < width; ++x)
{
switch(src->format->BitsPerPixel)
{
case 8:
pixel = *((Uint8*)src->pixels + y*src->pitch + x);
break;
case 16:
pixel = *((Uint16*)src->pixels + y*src->pitch/2 + x);
break;
case 32:
pixel = *((Uint32*)src->pixels + y*src->pitch/4 + x);
break;
default:
return -1;
}
if(pixel == 0 )
continue;
SDL_GetRGB(pixel, src->format, &r, &g, &b);
rgb2yuv(r, g, b, y1, u1, v1);
memset(dst->pixels[0] + (dstrect->y + y) * dst->pitches[0] + (dstrect->x + x),
(Uint8)y1, 1);
if((x%2 == 0 ) && (y%2 == 0 ))
{
memset(dst->pixels[1] + (uv_off + dstrect->y /2) * dst->pitches[1] + (dstrect->x/2 + x/2),
(Uint8)v1, 1);
memset(dst->pixels[2] + (uv_off + dstrect->y /2) * dst->pitches[2] + (dstrect->x/2 + x/2),
(Uint8)u1, 1);
}
}
if(y%2 == 0) ++uv_off;
}
return 0;
}
Related
in SDL we're trying to find the average colour of the screen. To do so we're reading all the pixel colour values and putting them into an array (Performance is not of concern), for some reason however, GetPixel always returns a colour (0,0,0,0). Ive already established that the RenderReadPixels works correctly since saving a screenshot works just fine.
const Uint32 format = SDL_PIXELFORMAT_ARGB8888;
SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, format);
SDL_RenderReadPixels(renderer, NULL, format, surface->pixels, surface->pitch);
float* coverage = new float[width*height]; // * allocates memory
coverage[0] = 1;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
SDL_Color col;
col = GetPixel(surface, i, j);
coverage[i * height + j] = (1/3)(col.r + col.b + col.g); //Return coverage value at i, j
std::cout << coverage[i * height + j]; //Always returns 0
std::cout << "\n";
}
}
SDL_Color GetPixel(SDL_Surface* srf, int x, int y)
{
SDL_Color color;
SDL_GetRGBA(get_pixel32(srf, x, y), srf->format, &color.r, &color.g, &color.b, &color.a);
return color;
}
Uint32 get_pixel32(SDL_Surface* surface, int x, int y)
{
//Convert the pixels to 32 bit
Uint32* pixels = (Uint32*)surface->pixels;
//Get the requested pixel
return pixels[(y * surface->w) + x];
}
1/3 is always 0 because of the way number promotion works in C++.
Best be explicit about what you want:
coverage[i * height + j] = float(col.r + col.b + col.g) / 3.0;
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.
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;
}
}
My goal is simple: I want to create a rendering system in C++ that can draw thousands of bitmaps on screen. I have been trying to use threads to speed up the process but to no avail. In most cases, I have actually slowed down performance by using multiple threads. I am using this project as an educational exercise by not using hardware acceleration. That said, my question is this:
What is the best way to use several threads to accept a massive list of images to be drawn onto the screen and render them at break-neck speeds? I know that I won’t be able to create a system that can rival hardware accelerated graphics, but I believe that my idea is still feasible because the operation is so simple: copying pixels from one memory location to another.
My renderer design uses three core blitting operations: position, rotation, and scale of a bitmap image. I have it set up to only rotate an image when needed, and only scale an image when needed.
I have gone through several designs for this system. All of them too slow to get the job done (300 64x64 bitmaps at barely 60fps).
Here are the designs I have tried:
Immediately drawing a source bitmap on a destination bitmap for every image on screen (moderate speed).
Creating workers that accept a draw instruction and immediately begin working on it while other workers receive their instructions also (slowest).
Workers that receive packages of several instructions at a time (slower).
Saving all drawing instructions up and then parting them up in one swoop to several workers while other tasks (in theory) are being done (slowest).
Here is the bitmap class I am using to blit bitmaps onto each other:
class Bitmap
{
public:
Bitmap(int w, int h)
{
width = w;
height = h;
size = w * h;
pixels = new unsigned int[size];
}
virtual ~Bitmap()
{
if (pixels != 0)
{
delete[] pixels;
pixels = 0;
}
}
void blit(Bitmap *bmp, float x, float y, float rot, float sclx,
float scly)
{
// Position only
if (rot == 0 && sclx == 1 && scly == 1)
{
blitPos(bmp, x, y);
return;
}
// Rotate only
else if (rot != 0 && sclx == 1 && scly == 1)
{
blitRot(bmp, x, y, rot);
return;
}
// Scale only
else if (rot == 0 && (sclx != 1 || scly != 1))
{
blitScl(bmp, x, y, sclx, scly);
return;
}
/////////////////////////////////////////////////////////////////////////////
// If it is not one of those, you have to do all three... :D
/////////////////////////////////////////////////////////////////////////////
// Create a bitmap that is scaled to the new size.
Bitmap tmp((int)(bmp->width * sclx), (int)(bmp->height * scly));
// Find how much each pixel steps:
float step_x = (float)bmp->width / (float)tmp.width;
float step_y = (float)bmp->height / (float)tmp.height;
// Fill the scaled image with pixels!
float inx = 0;
int xOut = 0;
while (xOut < tmp.width)
{
float iny = 0;
int yOut = 0;
while (yOut < tmp.height)
{
unsigned int sample = bmp->pixels[
(int)(std::floor(inx) + std::floor(iny) * bmp->width)
];
tmp.drawPixel(xOut, yOut, sample);
iny += step_y;
yOut++;
}
inx += step_x;
xOut++;
}
blitRot(&tmp, x, y, rot);
}
void drawPixel(int x, int y, unsigned int color)
{
if (x > width || y > height || x < 0 || y < 0)
return;
if (color == 0x00000000)
return;
int index = x + y * width;
if (index >= 0 && index <= size)
pixels[index] = color;
}
unsigned int getPixel(int x, int y)
{
return pixels[x + y * width];
}
void clear(unsigned int color)
{
std::fill(&pixels[0], &pixels[size], color);
}
private:
void blitPos(Bitmap *bmp, float x, float y)
{
// Don't draw if coordinates are already past edges
if (x > width || y > height || y + bmp->height < 0 || x + bmp->width < 0)
return;
int from;
int to;
int destfrom;
int destto;
for (int i = 0; i < bmp->height; i++)
{
from = i * bmp->width;
to = from + bmp->width;
//////// Caps
// Bitmap is being drawn past the right edge
if (x + bmp->width > width)
{
int cap = bmp->width - ((x + bmp->width) - width);
to = from + cap;
}
// Bitmap is being drawn past the left edge
else if (x + bmp->width < bmp->width)
{
int cap = bmp->width + x;
from += (bmp->width - cap);
to = from + cap;
}
//////// Destination Maths
if (x < 0)
{
destfrom = (y + i) * width;
destto = destfrom + (bmp->width + x);
}
else
{
destfrom = x + (y + i) * width;
destto = destfrom + bmp->width;
}
// Bitmap is being drawn past either top or bottom edges
if (y + i > height - 1)
{
continue;
}
if (destfrom > size || destfrom < 0)
{
continue;
}
memcpy(&pixels[destfrom], &bmp->pixels[from], sizeof(unsigned int) * (to - from));
}
}
void blitRot(Bitmap *bmp, float x, float y, float rot)
{
float sine = std::sin(-rot);
float cosine = std::cos(-rot);
int x1 = (int)(-bmp->height * sine);
int y1 = (int)(bmp->height * cosine);
int x2 = (int)(bmp->width * cosine - bmp->height * sine);
int y2 = (int)(bmp->height * cosine + bmp->width * sine);
int x3 = (int)(bmp->width * cosine);
int y3 = (int)(bmp->width * sine);
int minx = (int)std::min(0, std::min(x1, std::min(x2, x3)));
int miny = (int)std::min(0, std::min(y1, std::min(y2, y3)));
int maxx = (int)std::max(0, std::max(x1, std::max(x2, x3)));
int maxy = (int)std::max(0, std::max(y1, std::max(y2, y3)));
int w = maxx - minx;
int h = maxy - miny;
int srcx;
int srcy;
int dest_x;
int dest_y;
unsigned int color;
for (int sy = miny; sy < maxy; sy++)
{
for (int sx = minx; sx < maxx; sx++)
{
srcx = sx * cosine + sy * sine;
srcy = sy * cosine - sx * sine;
dest_x = x + sx;
dest_y = y + sy;
if (dest_x <= width - 1 && dest_y <= height - 1
&& dest_x >= 0 && dest_y >= 0)
{
color = 0;
// Only grab a pixel if it is inside of the src image
if (srcx < bmp->width && srcy < bmp->height && srcx >= 0 &&
srcy >= 0)
color = bmp->getPixel(srcx, srcy);
// Only this pixel if it is not completely transparent:
if (color & 0xFF000000)
// Only if the pixel is somewhere between 0 and the bmp size
if (0 < srcx < bmp->width && 0 < srcy < bmp->height)
drawPixel(x + sx, y + sy, color);
}
}
}
}
void blitScl(Bitmap *bmp, float x, float y, float sclx, float scly)
{
// Create a bitmap that is scaled to the new size.
int finalwidth = (int)(bmp->width * sclx);
int finalheight = (int)(bmp->height * scly);
// Find how much each pixel steps:
float step_x = (float)bmp->width / (float)finalwidth;
float step_y = (float)bmp->height / (float)finalheight;
// Fill the scaled image with pixels!
float inx = 0;
int xOut = 0;
float iny;
int yOut;
while (xOut < finalwidth)
{
iny = 0;
yOut = 0;
while (yOut < finalheight)
{
unsigned int sample = bmp->pixels[
(int)(std::floor(inx) + std::floor(iny) * bmp->width)
];
drawPixel(xOut + x, yOut + y, sample);
iny += step_y;
yOut++;
}
inx += step_x;
xOut++;
}
}
public:
int width;
int height;
int size;
unsigned int *pixels;
};
Here is some code showing the latest method I have tried: saving up all instructions and then giving them to workers once they have all been received:
class Instruction
{
public:
Instruction() {}
Instruction(Bitmap* out, Bitmap* in, float x, float y, float rot,
float sclx, float scly)
: outbuffer(out), inbmp(in), x(x), y(y), rot(rot),
sclx(sclx), scly(scly)
{ }
~Instruction()
{
outbuffer = nullptr;
inbmp = nullptr;
}
public:
Bitmap* outbuffer;
Bitmap* inbmp;
float x, y, rot, sclx, scly;
};
Layer Class:
class Layer
{
public:
bool empty()
{
return instructions.size() > 0;
}
public:
std::vector<Instruction> instructions;
int pixel_count;
};
Worker Thread Class:
class Worker
{
public:
void start()
{
done = false;
work_thread = std::thread(&Worker::processData, this);
}
void processData()
{
while (true)
{
controller.lock();
if (done)
{
controller.unlock();
break;
}
if (!layers.empty())
{
for (int i = 0; i < layers.size(); i++)
{
for (int j = 0; j < layers[i].instructions.size(); j++)
{
Instruction* inst = &layers[i].instructions[j];
inst->outbuffer->blit(inst->inbmp, inst->x, inst->y, inst->rot, inst->sclx, inst->scly);
}
}
layers.clear();
}
controller.unlock();
}
}
void finish()
{
done = true;
}
public:
bool done;
std::thread work_thread;
std::mutex controller;
std::vector<Layer> layers;
};
Finally, the Render Manager Class:
class RenderManager
{
public:
RenderManager()
{
workers.reserve(std::thread::hardware_concurrency());
for (int i = 0; i < 1; i++)
{
workers.emplace_back();
workers.back().start();
}
}
void layer()
{
layers.push_back(current_layer);
current_layer = Layer();
}
void blit(Bitmap* out, Bitmap* in, float x, float y, float rot, float sclx, float scly)
{
current_layer.instructions.emplace_back(out, in, x, y, rot, sclx, scly);
}
void processInstructions()
{
if (layers.empty())
layer();
lockall();
int index = 0;
for (int i = 0; i < layers.size(); i++)
{
// Evenly distribute the layers in a round-robin fashion
Layer l = layers[i];
workers[index].layers.push_back(layers[i]);
index++;
if (index >= workers.size()) index = 0;
}
layers.clear();
unlockall();
}
void lockall()
{
for (int i = 0; i < workers.size(); i++)
{
workers[i].controller.lock();
}
}
void unlockall()
{
for (int i = 0; i < workers.size(); i++)
{
workers[i].controller.unlock();
}
}
void finish()
{
// Wait until every worker is done rendering
lockall();
// At this point, we know they have nothing more to draw
unlockall();
}
void endRendering()
{
for (int i = 0; i < workers.size(); i++)
{
// Send each one an exit code
workers[i].finish();
}
// Let the workers finish and then return
for (int i = 0; i < workers.size(); i++)
{
workers[i].work_thread.join();
}
}
private:
std::vector<Worker> workers;
std::vector<Layer> layers;
Layer current_layer;
};
Here is a screenshot of what the 3rd method I tried, and it's results:
Sending packages of draw instructions
What would really be helpful is that if someone could simply point me in the right direction in regards to what method I should try. I have tried these four methods and have failed, so I stand before those who have done greater things than I for help. The least intelligent person in the room is the one that does not ask questions because his pride does not permit it. Please keep in mind though, this is my first question ever on Stack Overflow.
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;
}