ImageBuffer

BlitzMax Forums/BlitzMax Programming/ImageBuffer

Ist there something similar in BlitzMax as ImageBuffers in Blitz3D. For example something likt this in B3D:

image = CreateImage(100,100)
SetBuffer ImageBuffer(image)
Rect 10,10,90,90
SetBuffer BackBuffer()
DrawImage image,0,0


No, there isn't.

DrawRect 10, 10, 90, 90

Local image:TImage = CreateImage(100, 100)
GrabImage(image, 0, 0)

Cls

DrawImage image, 0, 0

Flip



Nicolas.

Problem is, GrabImage is dirt slow. This method also overwrites the image, so that you're not able to update an existing one. We really need a way to alter an image directly without drawing to the backbuffer. CopyRect would be nice too.

If you buy/have BlitzMax, this module will allow you to use normal 2D instead of the hardware accelerated stuff. It doesn't use Max2D so you'd have to do your own implementation of it -- I'm using this for software rendering. The code is licensed under the GNU Lesser General Public License (if you for some reason do not like the LGPL then you can jump off a bridge :D ).

ce_softdev.h
//	CEngine - 3D engine for graphics and game development
//	Copyright (C) 2005 Noel Raymond Cower
//	
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 of the License, or (at your option) any later version.
//	
//	This library is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//	Lesser General Public License for more details.
//	
//	You should have received a copy of the GNU Lesser General Public
//	License along with this library; if not, write to the Free Software
//	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//	    
//	To contact the author, e-mail him at noel_cower@... and/or hooker.with.a.penis@...

// Technically, this code has almost nothing to do with the software device
// All it's used for is 2D functionality that is otherwise nonexistant in BlitzMax

#ifndef _CE_SOFTDEV_H_
#define _CE_SOFTDEV_H_

// if i decide to not use SDL at some point in time, these bits might come in handy
#include "SDL/SDL.h"

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#	define CE_R_MASK 0xff000000
#	define CE_G_MASK 0x00ff0000
#	define CE_B_MASK 0x0000ff00
#	define CE_A_MASK 0x000000ff
#else
#	define CE_A_MASK 0xff000000
#	define CE_R_MASK 0x00ff0000
#	define CE_G_MASK 0x0000ff00
#	define CE_B_MASK 0x000000ff
#endif

#ifndef NULL
#	ifdef _cplusplus
#		define NULL 0
#	else
#		define NULL (void*)0
#	endif
#endif

#ifdef _cplusplus
extern "C" {
#endif

// For the sake of simplicity
#define ce_New(i) ((i*)malloc(sizeof(i)))
// And for the sake of consistency
#define ce_Delete(i) (free(i))

// A buffer or surface
typedef struct ce_Surface {
	SDL_Surface* surf;
	int locked;
	// Used for updating portions of the buffer
	int last_min_x, last_min_y;
	int last_max_x, last_max_y;
	int w;
	int h;
} ce_Surface;

extern ce_Surface* framebuffer;
extern ce_Surface* curBuffer;

// Used for updating portions of the framebuffer
extern int last_min_x, last_min_y, last_max_x, last_max_y;

// Initialize the software device
int ce_InitDevice(int, int, int, int);
// Dispose (free, delete, remove, destroy) the software device
int ce_DisposeDevice(void);

// Write a pixel to a buffer
int ce_WritePixel(int, int, int, int, int, int, ce_Surface*);
// Read a pixel from a buffer
int ce_ReadPixel(int, int, ce_Surface*);

// Get the framebuffer
ce_Surface* ce_GetFramebuffer(void);

// Creates a buffer
ce_Surface* ce_CreateBuffer(int, int, int);
// Disposes of a buffer
int ce_DisposeBuffer(ce_Surface*);

// Swaps the front and back buffers
int ce_SwapBuffers(void);

// Lock a buffer
int ce_LockBuffer(ce_Surface*);
// Unlock a buffer
int ce_UnlockBuffer(ce_Surface*);
// Get whether or not a buffer is locked
int ce_BufferIsLocked(ce_Surface*);

// Blits buffer src to buffer dst at X,Y
int ce_BlitBuffer(ce_Surface*, ce_Surface*, int, int);

// Fills a rectangular area of a buffer
int ce_FillRect(ce_Surface*, int, int, int, int, int, int, int, int);

// Sets a buffer to a single color
int ce_ClearBuffer(ce_Surface*, int, int, int, int);

#ifdef _cplusplus
}
#endif

#endif //_CE_SOFTDEV_H_


ce_softdev.c
//	CEngine - 3D engine for graphics and game development
//	Copyright (C) 2005 Noel Raymond Cower
//	
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 of the License, or (at your option) any later version.
//	
//	This library is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//	Lesser General Public License for more details.
//	
//	You should have received a copy of the GNU Lesser General Public
//	License along with this library; if not, write to the Free Software
//	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//	    
//	To contact the author, e-mail him at noel_cower@... and/or hooker.with.a.penis@...

// Technically, this code has almost nothing to do with the software device
// All it's used for is 2D functionality that is otherwise nonexistant in BlitzMax

#include "ce_softdev.h"
#include "stdio.h"

static ce_Surface* framebuffer = NULL;
static ce_Surface* curBuffer = NULL;

int ce_InitDevice(int w, int h, int bpp, int fs) {
	if (framebuffer!=NULL)
		return -2;	// Cannot initialize it more than once
	
	int sdlFlags = SDL_SWSURFACE | SDL_DOUBLEBUF;
	if (fs>0)
		sdlFlags|=SDL_FULLSCREEN;
	
	SDL_Init(SDL_INIT_VIDEO);
	
	SDL_WM_SetCaption("CEngine Software Window", "CEngine Software Window");
	
	SDL_Surface* surf = SDL_SetVideoMode(w,h,bpp,sdlFlags);
	if (surf==NULL) {
		fprintf(stderr,
			"Failed to create display device at %i*%i at %i BPP with %i for fullscreen, error is "%s"\n",
			w, h, bpp, fs, SDL_GetError());
		return -1;
	}
	
	framebuffer = ce_New(ce_Surface);
	framebuffer->last_min_x = 0;
	framebuffer->last_min_y = 0;
	framebuffer->last_max_x = 0;
	framebuffer->last_max_y = 0;
	framebuffer->locked = 0;
	framebuffer->surf = surf;

	framebuffer->w = w;
	framebuffer->h = h;
	
	curBuffer = framebuffer;
	
	return 0;
}

int ce_DisposeDevice(void) {
	SDL_Quit();
	framebuffer = NULL;
	curBuffer = NULL;
}

int ce_SwapBuffers(void) {
	if (framebuffer->locked > 0)
		return -1;
	
	//SDL_UpdateRect(framebuffer->surf,0,0,framebuffer->w,framebuffer->h);
	
	SDL_Flip(framebuffer->surf);
	return 0;
}

ce_Surface* ce_CreateBuffer(int w, int h, int bpp) {
	SDL_Surface* i = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, bpp,
		CE_R_MASK, CE_G_MASK, CE_B_MASK, CE_A_MASK);
	if (i == NULL)
		return NULL;
	ce_Surface* surf = ce_New(ce_Surface);
	surf->surf = i;
	surf->w = w;
	surf->h = h;
	surf->last_min_x = 0;
	surf->last_min_y = 0;
	surf->last_max_x = 0;
	surf->last_max_y = 0;
	surf->locked = 0;
	return surf;
}

ce_Surface* ce_GetFramebuffer(void) {
	return framebuffer;
}

int ce_DisposeBuffer(ce_Surface* surf) {
	SDL_FreeSurface(surf->surf);
	ce_Delete(surf);
	return 0;
}

int ce_WritePixel(int x, int y, int r, int g, int b, int a, ce_Surface* surf) {
	if (surf->locked==0)
		if (SDL_LockSurface(surf->surf)!=0)
			return -1;
	
	Uint32 pixel = SDL_MapRGBA(surf->surf->format,r,g,b,a);
	
	// Mostly just going off of the example in the SDL reference manual
	Uint8* bits = ((Uint8*)(surf->surf->pixels) + y*surf->surf->pitch + x*surf->surf->format->BytesPerPixel);
	
	switch (surf->surf->format->BytesPerPixel) {
		case 1:
			*((Uint8*)bits) = (Uint8)pixel;
		break;
		case 2:
			*((Uint16*)bits) = (Uint16)pixel;
		break;
		case 3: {
				Uint8 r,g,b;
				
				r=(pixel>>surf->surf->format->Rshift)&0xFF;
				g=(pixel>>surf->surf->format->Gshift)&0xFF;
				b=(pixel>>surf->surf->format->Bshift)&0xFF;
				
				*(bits + surf->surf->format->Rshift/8) = r;
				*(bits + surf->surf->format->Gshift/8) = g;
				*(bits + surf->surf->format->Bshift/8) = b;
			}
		break;
		case 4:
			*((Uint32*)bits) = (Uint32)pixel;
		break;
	}
	
	if (surf->locked==0) {
		SDL_UnlockSurface(surf->surf);
	//	SDL_UpdateRect(surf->surf,x,y,1,1);
	} else {
		// set update region
		if (x < surf->last_min_x)
			surf->last_min_x = x;
		if (y < surf->last_min_y)
			surf->last_min_y = y;
		
		if (x > surf->last_max_x)
			surf->last_max_x = x;
		if (y > surf->last_max_y)
			surf->last_max_y = y;
		
		if (surf->last_max_y == surf->last_min_y)
			surf->last_max_y++;
		if (surf->last_max_x == surf->last_min_x)
			surf->last_max_x++;
	}
}

int ce_ReadPixel(int x, int y, ce_Surface* surf) {
	SDL_Surface* i = surf->surf;
	
	if (surf->locked == 0)
		if (SDL_LockSurface(i)!=0)
			return 0;
	
	Uint32 pix = *(Uint32*)((Uint8*)i->pixels + y*i->pitch + x*i->format->BytesPerPixel);

	//Uint32 pixel = pix;
	//Uint8 *colors = (Uint8*)pixel;

	//SDL_GetRGBA(pix, i->format, colors, colors+1, colors+2, colors+3);

	if (surf->locked == 0)
		SDL_UnlockSurface(i);

	return pix;
}

int ce_BufferIsLocked(ce_Surface* surf) {
	return (surf->locked>0);
}

int ce_LockBuffer(ce_Surface* surf) {
	int res = SDL_LockSurface(surf->surf);
	if (res == 0)
		surf->locked++;
	return res;
}

int ce_UnlockBuffer(ce_Surface* surf) {
	if (surf->locked == 0)
		return 0;
	
	SDL_UnlockSurface(surf->surf);
	surf->locked--;
	
	if (surf->locked==0) {
		SDL_Rect r;
		r.x = surf->last_min_x;
		r.y = surf->last_min_y;
		r.w = surf->last_max_x-surf->last_min_x;
		r.h = surf->last_max_y-surf->last_min_y;
		
		if (r.w == 0 || r.h == 0)
			return 0;
		
		if (r.x < 0) {
			r.w += r.x;
			r.x = 0;
		}
		
		if (r.y < 0) {
			r.h += r.y;
			r.y = 0;
		}
		
	//	SDL_UpdateRect(surf->surf,r.x,r.y,r.w,r.h);
	}
	
	return 0;
}

int ce_FillRect(ce_Surface* surf, int x, int y, int w, int h, int r, int g, int b, int a) {
	SDL_Surface* i = surf->surf;
	SDL_Rect rc;
	
	if (x < 0) {
		w += x;
		x = 0;
	}
	
	if (y < 0) {
		h += y;
		y = 0;
	}
	
	rc.x = x; rc.y = y;
	rc.w = w; rc.h = h;
	
	if (rc.w == 0 || rc.h == 0)
		return 0;
	
	if (surf->locked==0)
		if (SDL_LockSurface(i)!=0)
			return -1;
	
	if (SDL_FillRect(i,&rc,SDL_MapRGBA(surf->surf->format,r,g,b,a))!=0) {
		if (surf->locked==0)
			SDL_UnlockSurface(i);
		return -1;
	}
	
	if (surf->locked==0) {
		SDL_UnlockSurface(i);
	//	SDL_UpdateRect(i,x,y,w,h);
	}
	
	return 0;
}

int ce_ClearBuffer(ce_Surface* surf, int r, int g, int b, int a) {
	SDL_Surface* i = surf->surf;
	
	if (surf->locked==0)
		if (SDL_LockSurface(i)!=0)
			return -1;
	
	if (SDL_FillRect(i,NULL,SDL_MapRGBA(surf->surf->format,r,g,b,a))!=0) {
		if (surf->locked==0)
			SDL_UnlockSurface(i);
		return -1;
	}
	
	if (surf->locked==0) {
		SDL_UnlockSurface(i);
	//	SDL_UpdateRect(i,0,0,surf->w,surf->h);
	}
	
	return 0;
}

int ce_BlitBuffer(ce_Surface* src, ce_Surface* dst, int x, int y) {
	SDL_Rect ra;
	SDL_Rect rb;
	
	ra.x = 0;

	ra.y = 0;
	ra.w = src->w;
	ra.h = src->h;
	
	rb.x = x;
	rb.y = y;
	rb.w = ra.w;
	rb.h = ra.h;
	
	return SDL_BlitSurface(src->surf,&ra,dst->surf,&rb);
}


softdevice.bmx (mod/cengine.mod/softdevice.mod/softdevice.bmx)
'#Region Copyright and license
'	CEngine - 3D engine for graphics and game development
'	Copyright 2005 Noel R. Cower
'
'	This library is free software; you can redistribute it and/or
'	modify it under the terms of the GNU Lesser General Public
'	License as published by the Free Software Foundation; either
'	version 2.1 of the License, or (at your option) any later version.
'	
'	This library is distributed in the hope that it will be useful,
'	but WITHOUT ANY WARRANTY; without even the implied warranty of
'	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
'	Lesser General Public License for more details.
'	
'	You should have received a copy of the GNU Lesser General Public
'	License along with this library; if not, write to the Free Software
'	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
'
'	To contact the author, e-mail him at noel_cower@... and/or hooker.with.a.penis@...
'#End Region

Strict

Module CEngine.SoftDevice
ModuleInfo "CEngine software device class"
ModuleInfo "Uses SDL (www.libsdl.org VISIT THEM) for drawing"
ModuleInfo "Written by Noel R. Cower"

'Import CEngine.EngineCore

Import "-lSDL"
Import "ce_softdev.c"

Extern "C"
	Function ce_InitDevice(w:Int, h:Int, bpp:Int, fs:Int)
	Function ce_DisposeDevice()
	
	Function ce_CreateBuffer:Byte Ptr(w:Int,h:Int,bpp:Int)
	Function ce_DisposeBuffer(buffer:Byte Ptr)
	
	Function ce_GetFramebuffer:Byte Ptr()
	
	Function ce_ReadPixel(x:Int,y:Int,buf:Byte Ptr)
	Function ce_WritePixel(x:Int,y:Int,r:Int, g:Int, b:Int, a:Int,buf:Byte Ptr)
	
	Function ce_LockBuffer(buf:Byte Ptr)
	Function ce_UnlockBuffer(buf:Byte Ptr)
	
	Function ce_BufferIsLocked(buf:Byte Ptr)
	
	Function ce_SwapBuffers()
	
	Function ce_FillRect(surf:Byte Ptr, x:Int, y:Int, w:Int, h:Int, r:Int, g:Int, b:Int, a:Int)
	Function ce_ClearBuffer(surf:Byte Ptr, r:Int, g:Int, b:Int, a:Int)
	
	Function ce_BlitBuffer(src:Byte Ptr, dst:Byte Ptr, x:Int, y:Int)
End Extern

Type CSoftwareBuffer
	Global _framebuffer:CSoftwareBuffer
	
	Field _ptr:Byte Ptr
	
	Method GetWidth()
		Return (Int Ptr(_ptr+24))[0];
	End Method
	
	Method GetHeight()
		Return (Int Ptr(_ptr+28))[0];
	End Method
	
	Method Lock()
		ce_LockBuffer(_ptr)
	End Method
	
	Method Unlock()
		ce_UnlockBuffer(_ptr)
	End Method
	
	Method IsLocked()
		Return ce_BufferIsLocked(_ptr)
	End Method
	
	Method LockLevel()
		Return (Int Ptr(_ptr+4))[0]
	End Method
	
	Method WritePixel(x,y,r,g,b,a)
		Return ce_WritePixel(x,y,r,g,b,a,_ptr)
	End Method
	
	Method ReadPixel(x,y,r:Byte Ptr = Null,g:Byte Ptr = Null,b:Byte Ptr = Null,a:Byte Ptr = Null)
		Local ret = ce_ReadPixel(x,y,_ptr)
		
		If r <> Null Or g <> Null Or b <> Null Or a <> Null
			Local i:Byte Ptr = Byte Ptr(Varptr(ret))
			
			If r <> Null Then r[0] = i[0]
			If g <> Null Then g[0] = i[1]
			If b <> Null Then b[0] = i[2]
			If a <> Null Then a[0] = i[3]
		EndIf
		
		Return ret
	End Method
	
	Method FillRect(x,y,w,h,r,g,b,a)
		Return ce_FillRect(_ptr,x,y,w,h,r,g,b,a)
	End Method
	
	Method Clear(r,g,b,a)
		Return ce_ClearBuffer(_ptr,r,g,b,a)
	End Method
	
	Method Blit(toSurf:CSoftwareBuffer,X:Int,Y:Int)
		Return ce_BlitBuffer(_ptr,toSurf._ptr,X,Y)
	End Method
	
	Method Delete()
		ce_DisposeBuffer(_ptr)
		_ptr = Null
	End Method
	
	Function Create:CSoftwareBuffer(Width:Int, Height:Int, BPP:Int=32)
		Local _ptr:Byte Ptr = ce_CreateBuffer(Width,Height,BPP)
		If _ptr = Null Then Return Null
		Local i:CSoftwareBuffer = New CSoftwareBuffer
		i._ptr = _ptr
		Return i
	End Function
	
	Function FrameBuffer:CSoftwareBuffer()
		If _framebuffer = Null And ce_GetFramebuffer() <> Null
			_framebuffer = New CSoftwareBuffer
			_framebuffer._ptr = ce_GetFramebuffer()
		EndIf
		
		Return _framebuffer
	End Function
End Type


Use a pixmap to alter the image. Maverick69 is only asking for an equivalent of imagebuffer and I'm afraid grabing is the way to do it and yes this is extremely slow :).


Nicolas.

Use a pixmap to alter the image.

How would you go about drawing a random rect directly to a PixMap/Image every loop, without clearing it, and display it? I am new to PixMaps and would appreciate an example.

That would be impossible whit the current BlitzMax featureset. The only way you could produce this effect is drawing the image to the backbuffer then drawing the rectangle on top of it and then grabing it again without actually flipping the backbuffer. Or you could use direct OpenGL. Don't ask me for an example, I can't do it. Don't get me wrong, I fully agree whit you.


Nicolas.

That would be impossible whit the current BlitzMax featureset.

OK, that's what I wanted to know. I thought you were saying this was possible with the aid of PixMaps. Thanks for clearing that up.

Now we just need someone to realize how big of a problem this is and fix it...

You could go to OpenGL and look at using glCopyTexSubImage2D. It does not overwrite an existing texture - you set up a dummy texture, it allocate the memory, and then use that call to grab an area from the backbuffer to an area within that texture (doesn't have to be the whole texture size). It is apparently faster than a regular grab image. Still has some slowdown though.

It would be nice if Blitz Research would use that and add some Max2D commands to take advantage of it - ie GrabToImage or something.