Game Development Community

Manipulating GBitmap's Pixels?

by Paul Griffiths · in Torque Game Engine · 05/31/2006 (10:13 pm) · 18 replies

I'm building a web-cam capture add on to torque which i have images saved to files working. Now i wish to render the image to a GUI control i have ready.
Question is, how do you set a GBitmap's pixel color?
Ive copied the Theora GUI control and riped out the Theora library and put in vidCapture library instead.
My question is in the code below.


void VideoCaptureTexture::drawFrame()
{

	GBitmap *bmp = mTextureHandle->getBitmap();
	U8* dst = bmp->getAddress(0, 0);


	for(S32 y = 0; y < bmp->getHeight(); y += 1)
	{

		for(S32 x = 0; x < bmp->getWidth(); x += 1)
		{
		   // how do i set it's pixel color here? to say red?	
		}
 
	}
}

Thanks for your time.

#1
05/31/2006 (10:54 pm)
for(S32 y = 0; y < bmp->getHeight(); y += 1)
{
	for(S32 x = 0; x < bmp->getWidth(); x += 1)
	{
		// how do i set it's pixel color here? to say red?	
		ColorI col(255,0,0,0); // R G B A 
		dst->setColor(x,y, col);
	}
}

Alternatively you could set manually the color components for "col" like this:
col.red = 255; // red value
col.green = 255; // green
col.blue = 255; //blue

Does this help?

Martin
#2
06/01/2006 (12:42 am)
Yes, thanks martin, i didnt know it was that simple, should be a breeze! i hope :)
#3
06/01/2006 (12:43 am)
Question though, how does dst increase?
#4
06/01/2006 (12:47 am)
Im receiving an error:

C:\Torque\SDK\engine\TMessenger\videoCapturePlayer.cc(101): error C2227: left of '->setColor' must point to class/struct/union

any ideas?
#5
06/01/2006 (12:56 am)
Ups, sorry, used the wrong variable. it should be
[b]bmp[/b]->setColor(x,y, col);
#6
06/01/2006 (12:58 am)
I imagine you'd be better off using

U8 *pBmp = bmp->getWritableBits(0);

to get a pointer through which you can manipulate the bmp data, rather than doing everything through the setColor function
#7
06/01/2006 (1:02 am)
Sam, can you complete my example?
#8
06/01/2006 (1:11 am)
void VideoCaptureTexture::drawFrame()
{

	GBitmap *bmp = mTextureHandle->getBitmap();
	U8 *pBmp = bmp->getWritableBits(0);
        U8 *pxLoc;
	U32 w = bmp->getWidth(0);
	U32 h = bmp->getHeight(0);
        bool hasAlpha = (bmp->bytesPerPixel == 4);

        for (U32 x=0; x<w; xx++)
	{
		for (U32 y=0; y<h; yy++)
		{
			pxLoc = pBmp+(((y * w) + x) * bmp->bytesPerPixel);

			pxLoc[0] = 255;  // red
			pxLoc[1] = 0;  // green
                        pxLoc[2] = 0;  // blue
                        if (hasAlpha)
                           pxLoc[3] = 255; // alpha
		}
	}
}
#9
06/01/2006 (1:19 am)
All i'm getting is white, the same as before i changed the pixels. Can anyone see anything im missing?

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "videoCapturePlayer.h"
#include "math/mMath.h"
#include "util/safeDelete.h"

//-----------------------------------------------------------------------------


VideoCaptureTexture::VideoCaptureTexture()
{
	init();
}


void VideoCaptureTexture::init()
{
	mReady = false;
	mPlaying = false;
	mPlayThread = NULL;
	mTextureHandle = NULL;
}


VideoCaptureTexture::~VideoCaptureTexture()
{
	destroyTexture();
}


// tears down anything the texture has
void VideoCaptureTexture::destroyTexture(bool restartOgg)
{
	mPlaying = false;

	// kill the thread if its playing
	SAFE_DELETE(mPlayThread);

	// Set us to a null state.
	mReady = false;

	SAFE_DELETE(mTextureHandle);
}


bool VideoCaptureTexture::createVideoBuffers()
{
	// Set up our texture.
	const GBitmap *bmp = new GBitmap(
		320,
		240,
		false, GBitmap::RGB);

	mTextureHandle = new TextureHandle(NULL, bmp, true);
	return true;
}


void VideoCaptureTexture::drawFrame()
{

	// get destination buffer (and 1 row offset)
	GBitmap *bmp = mTextureHandle->getBitmap();

	Con::printf("Drawing to bmp width:" + bmp->getWidth());

	for(S32 y = 0; y < bmp->getHeight(); y += 1)
	{
		for(S32 x = 0; x < bmp->getWidth(); x += 1)
		{
			ColorI col(0,255,0,0); // R G B A 
			bmp->setColor(x,y, col);
			//Con::printf("setting pixel");
		}
	}
}


bool VideoCaptureTexture::play()
{
	mReady = true;
	if(!mPlaying)
		mPlayThread = new Thread((ThreadRunFunction)playThread, (S32) this, 1);

	return mPlayThread;
}


void VideoCaptureTexture::stop()
{
	mPlaying = false;

	if(mPlayThread)
	{
		SAFE_DELETE(mPlayThread);
	}
}


void VideoCaptureTexture::playThread( void *udata )
{
	VideoCaptureTexture* pThis = (VideoCaptureTexture *)udata;
	pThis->playLoop();
}


bool VideoCaptureTexture::playLoop()
{
	createVideoBuffers();
	mPlaying = true;
	Con::printf("######################## Start of loop ###########################");
	// time to draw the frame!
	drawFrame();  
	refresh();
	return false;
}


// returns a handle for OpenGL calls
U32 VideoCaptureTexture::getGLName()
{
	if(mTextureHandle)
	{
		mTextureHandle->refresh();
		return mTextureHandle->getGLName();
	}
	return 0;
}

// copies the newest texture data to openGL video memory
void VideoCaptureTexture::refresh()
{
	if(mTextureHandle)
		mTextureHandle->refresh();
}
#10
06/01/2006 (1:44 am)
Sam even with your example itonly renders white.

Heres also my gui control:

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "gui/core/guiControl.h"
#include "guiVideoCaptureCtrl.h"
#include "console/consoleTypes.h"
#include "dgl/dgl.h"

 
//----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiVideoCaptureCtrl);

GuiVideoCaptureCtrl::GuiVideoCaptureCtrl()
{
	mFilename      = StringTable->insert("");
	mDone          = false;
	mStopOnSleep   = false;
	mBackgroundColor.set(0,0,0);
}

//----------------------------------------------------------------------------
GuiVideoCaptureCtrl::~GuiVideoCaptureCtrl()
{
}

void GuiVideoCaptureCtrl::initPersistFields()
{
	Parent::initPersistFields();

	addGroup("Playback");

	addField("videoCaptureFile",  TypeFilename,  Offset(mFilename,          GuiVideoCaptureCtrl));
	addField("done",        TypeBool,      Offset(mDone,              GuiVideoCaptureCtrl));
	addField("stopOnSleep", TypeBool,      Offset(mStopOnSleep,       GuiVideoCaptureCtrl));
	addField("backgroundColor", TypeColorI,Offset(mBackgroundColor,   GuiVideoCaptureCtrl));

	endGroup("Playback");
}

ConsoleMethod( GuiVideoCaptureCtrl, setFile, void, 3, 3, "(string filename) Set an Ogg VideoCapture file to play.")
{
	object->setFile(argv[2]);
}

ConsoleMethod( GuiVideoCaptureCtrl, stop, void, 2, 2, "() Stop playback.")
{
	object->stop();
}

ConsoleMethod( GuiVideoCaptureCtrl, getCurrentTime, F32, 2, 2, "() Return the time elapsed in playback, in seconds.")
{
	return object->getCurrentTime();
}


ConsoleMethod( GuiVideoCaptureCtrl, initializeVideoCamera, void, 0, 0, "Initialize camera.")
{
	object->initializeVideoCamera();
}


void GuiVideoCaptureCtrl::initializeVideoCamera()
{
	Con::printf("Initializing GUI video capture object.\n");
	mVideoCaptureTexture.play();
}

 

void GuiVideoCaptureCtrl::setFile(const char* szFilename)
{
	mDone = false;
	//if(szFilename && szFilename[0])
	//	mVideoCaptureTexture.setFile(szFilename, true);
}

void GuiVideoCaptureCtrl::stop()
{
	mVideoCaptureTexture.stop();
	mDone = true;
}

//----------------------------------------------------------------------------
bool GuiVideoCaptureCtrl::onWake()
{
	if (!Parent::onWake()) return false;

	if(mVideoCaptureTexture.isReady())
		return true;

	setFile(mFilename);

	return true;
}

//----------------------------------------------------------------------------
void GuiVideoCaptureCtrl::onSleep()
{
	Parent::onSleep();

	if(mStopOnSleep)
		stop();
}

//----------------------------------------------------------------------------
void GuiVideoCaptureCtrl::onRender(Point2I offset, const RectI &updateRect)
{
	const RectI rect(offset, mBounds.extent);

	if(mVideoCaptureTexture.isReady() && mVideoCaptureTexture.isPlaying())
	{
		mVideoCaptureTexture.refresh();
		dglDrawRectFill(rect, mBackgroundColor); // black rect
		dglClearBitmapModulation();

		dglDrawBitmapStretch(mVideoCaptureTexture, rect);
	}
	else
	{
		if(mVideoCaptureTexture.isReady())
			mDone = true;

		dglDrawRectFill(rect, mBackgroundColor); // black rect
	}

	dglDrawRect(rect, mBackgroundColor);
	renderChildControls(offset, updateRect);
}

//----------------------------------------------------------------------------
void GuiVideoCaptureCtrl::inspectPostApply()
{
	stop();
	setFile(mFilename);
}
#11
06/01/2006 (2:03 am)
Hmm... this approach has worked for me in getting real-time screen-captured bitmaps onto a Torque texture, and also for blending textures.

Have you checked that mTextureHandle->refresh() works properly?
What I have been using is TextureManager::registerTexture to re-register the texture after it has changed.

Here's one of the variations I have used, in this case replacing an in-game texture (texturefilename) with another one (pixels) copied from another window's canvas using a Windows API call, and therefore in Windows-format BMP*:

The texture has to be registered as a BitmapKeepTexture, otherwise it's deleted from the engine after it has been sent to the graphics card.

bool replaceTexture(const char* texturefilename, U8* pixels, int w, int h)
{
	texturefilename = StringTable->insert(texturefilename);
	TextureObject* t = TextureDictionary::find(texturefilename, BitmapKeepTexture, 1);
	GBitmap* bmp;
	int x, y;

	if (t)
	{	// bitmap is already in memory, probably modified already since being loaded
		bmp = t->bitmap;
	}
	else   // this is the 1st time this texture is being modified => reload image from disk
	{
		t = TextureDictionary::find(texturefilename);
		if (!t)
			return false;  // cannot find texture

		bmp = TextureManager::loadBitmapInstance(texturefilename);
		t->type=BitmapKeepTexture;
	}

	U8 *pBmpLoc0 = bmp->getWritableBits(0);
	U8 *pLoc, *pBmpLoc;
	int bytesperpixel = bmp->bytesPerPixel;
	for (x=0; x<w; x++)
	{
		for (y=0; y<h; y++)
		{
			pLoc = pixels+(((x) + ((h-1-y)*w))*4); // pixel in windows BMP (flipped y axis) 	
			pBmpLoc = pBmpLoc0+((y*w)+x)*bytesperpixel; // pixel in texture
			pBmpLoc[0]=pLoc[2];  // r
			pBmpLoc[1]=pLoc[1];  // g
			pBmpLoc[2]=pLoc[0];  // b
		}
	}

	t->bitmap=bmp;
	TextureManager::registerTexture(texturefilename, bmp, BitmapKeepTexture, 1);

	return (!(t==NULL));
}

* Windows BMPs have flipped y axes and also reversed RGB packing
#12
06/01/2006 (2:16 am)
>Have you checked that mTextureHandle->refresh() works properly?

hard to tell, I included a Con::printf in the refresh function and it gets called.
#13
06/01/2006 (2:19 am)
I really want to avoid manipulating an existing texture as different cameras capture at different resolutions.
#14
06/01/2006 (2:39 am)
The bmap is not being deleted as i can get it's width/height at any time.
Nowhere in the theora code is there TextureManager::registerTexture so im guessing should not be needed in my case?
#15
06/01/2006 (4:32 am)
Any ideas anyone? im completly stuck.
#16
06/01/2006 (9:26 pm)
Can you create a new texture resource and not manipulate an existing one?
The theora plugin creates it's own so so should i should be able to too.
I'm looking for a pro to answer my question dated Jun 01, 2006 09:19.
I can't see anything im missing? It compiles fine and does not crash but my gui just turns white after i call GuiVideoCaptureCtrl::initializeVideoCamera().

I hope to release my code as a new resource but i just need this part answered.
#17
06/04/2006 (1:44 pm)
I have Webcam Capture to Torque GUI working nicely, screenshot here: pgmedia.game-host.org/Site/TMessenger/screenshot.php
#18
11/12/2009 (5:06 pm)
@Paul, how did you end up copying the CVImage to a bitmap?