⛏️ index : haiku.git

#include <Application.h>
#include <WindowScreen.h>
#include <Screen.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <SupportDefs.h> // min_c() and max_c()

#ifdef DEBUGGING
#define PRINT(x) printf x
#else
#define PRINT(x)
#endif

// macros
#define set_pixel(x,y,color) (frame_buffer[x + (line_length*y)] = color)
#define get_pixel(x,y) (frame_buffer[x + (line_length*y)])

class NApplication : public BApplication {
public:
	NApplication();
	bool is_quitting; // So that the WindowScreen knows what
                     //       to do when disconnected.
private:
	bool QuitRequested();
	void ReadyToRun();
};

class NWindowScreen : public BWindowScreen {
public:
	NWindowScreen(status_t*);
private:
	void ScreenConnected(bool);
	int32 MyCode();
	static int32 Entry(void*);
	// handy stuff
	void set_frame_rate(float fps) {frame_pause = (bigtime_t)((1000 * 1000)/fps);}
	// special for demos
	enum {
		// used for star->last_draw
			INVALID	= 0x7fffffff
	};
	typedef struct {
		float init_velocity;
		float gravity;
		double cos_z_theta;
		int32 y;
		int32 x;
		int32 timeval;
		uint32 last_draw;
		int32 lx,ly;
	} particle;
	uint32 particle_count;
	particle *particle_list;
	// simple raster functions
	void draw_line(int x1, int y1, int x2, int y2, int color);
	void draw_rect(int x, int y, int w, int h, int color);
	void fill_rect(int x, int y, int w, int h, int color);
	void draw_ellipse(int cx, int cy, int wide, int deep, int color);
	void fill_ellipse(int x, int y, int xradius, int yradius, int color);
	void ellipse_points(int x, int y, int x_offset, int y_offset, int color);
	void ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color);
	thread_id tid;
	sem_id sem;
	area_id area;
	uint8* save_buffer;
	uint8* frame_buffer;
	ulong line_length;
	bigtime_t frame_pause; // time between frames
	int width,height;
	int COLORS;
	bool thread_is_locked;	// small hack to allow to quit the
							// 	app from ScreenConnected()
};

int
main()
{
	NApplication app;
	return 0;
}

NApplication::NApplication()
      :BApplication("application/x-vnd.Prok-DemoTemplate")
{
	Run(); // see you in ReadyToRun()
}

void NApplication::ReadyToRun()
{
	PRINT(("ReadyToRun()\n"));
	status_t ret = B_ERROR;
	is_quitting = false;
	NWindowScreen *ws = new NWindowScreen(&ret);
	PRINT(("WindowScreen ctor returned. ret = %s\n", strerror(ret)));
	// exit if constructing the WindowScreen failed.
	if((ws == NULL) || (ret < B_OK))
	{
		//printf("the window screen was NULL, or there was an error\n");
		PostMessage(B_QUIT_REQUESTED);
	}
	else
		PRINT(("everything's just peachy. done with ReadyToRun().\n"));
}

bool NApplication::QuitRequested()
{
	PRINT(("QuitRequested()\n"));
	status_t ret;
	is_quitting = true;
	wait_for_thread(find_thread("rendering thread"), &ret); // wait for the render thread to finish	
	return true;
}

NWindowScreen::NWindowScreen(status_t *ret)
	: BWindowScreen("Example", B_8_BIT_640x480, ret), width(639), height(479), COLORS(256)
{
	PRINT(("WindowScreen ctor.\n"));
	thread_is_locked = true;
	tid = 0;
	if(*ret == B_OK)
	{
		PRINT(("creating blocking sem and save_buffer area.\n"));
		// this semaphore controls the access to the WindowScreen
		sem = create_sem(0,"WindowScreen Access");
		// this area is used to save the whole framebuffer when
		//       switching workspaces. (better than malloc()).
		area = create_area("save", (void**)&save_buffer, B_ANY_ADDRESS, 640*480, B_NO_LOCK, B_READ_AREA|B_WRITE_AREA);
		// exit if an error occured.
		if((sem < B_OK) || (area < B_OK))
		{
			PRINT(("create_area() or create_sem() failed\n"));
			*ret = B_ERROR;
		}
		else
		{
			PRINT(("calling Show().\n"));
			Show(); // let's go. See you in ScreenConnected.
		}
	}
	else
	{
		PRINT(("BWindowScreen base class ctor returned failure\n"));
		be_app->PostMessage(B_QUIT_REQUESTED);
	}
	// set the frame rate
	set_frame_rate(30.);
}


void
NWindowScreen::ScreenConnected(bool connected)
{
	PRINT(("ScreenConnected()\n"));
	fflush(stdout);
	if(connected)
	{
		if(SetSpace(B_8_BIT_640x480) < B_OK)
		{
			//SetFrameBuffer(640, 480);
			PRINT(("SetSpace() failed\n"));
			// properly set the framebuffer. exit if an error occurs.
			be_app->PostMessage(B_QUIT_REQUESTED);
			return;
		}
		// get the framebuffer-related info, each time the
		// WindowScreen is connected (multiple monitor)
		frame_buffer = (uint8*)(CardInfo()->frame_buffer);
		line_length = FrameBufferInfo()->bytes_per_row;
		if(tid == 0)
		{
			// clean the framebuffer
			PRINT(("zeroing the framebuffer\n"));
			memset(frame_buffer,0,480*line_length);
			// spawn the rendering thread. exit if an error occurs.
			PRINT(("spawning the render thread.\n"));
			tid = spawn_thread(Entry,"rendering thread", B_URGENT_DISPLAY_PRIORITY,this);
			if(resume_thread(tid) < B_OK)
			{
				be_app->PostMessage(B_QUIT_REQUESTED);
				return;
			}
		}
		else
		{
			for(int y=0;y<480;y++)
			{
				// restore the framebuffer when switching back from
				// another workspace.
            	memcpy(frame_buffer+y*line_length,save_buffer+640*y,640);
            }
		}
		// set our color list.
		rgb_color palette[256];
		rgb_color c1;
		for(int i=0,j=0;i<256;i++,j++)
		{
			if(i<64)
			{
				c1.red = j*4; // greys
				c1.green = j*4;
				c1.blue = j*4;
				c1.alpha = 255;
			}
			if((i>=64) && (i<128))
			{
				c1.red = j*4; // reds
				c1.green = 0;
				c1.blue = 0;
				c1.alpha = 255;
			}
			if((i>=128) && (i<192))
			{
				c1.red = 0; // greens
				c1.green = j*4;
				c1.blue = 0;
				c1.alpha = 255;
			}
			if((i>=192) && (i<256))
			{
				c1.red = 0; // blues
				c1.green = 0;
				c1.blue = j*4;
				c1.alpha = 255;
			}
			if(j == 64)
				j=0;
			palette[i]=c1;
		}
		SetColorList(palette);
		
		// allow the rendering thread to run.
		thread_is_locked = false;
		release_sem(sem);
	}
	else /* !connected */
	{
		// block the rendering thread.
		if(!thread_is_locked)
		{
			acquire_sem(sem);
			thread_is_locked = true;
		}
		// kill the rendering and clean up when quitting
		if((((NApplication*)be_app)->is_quitting))
		{
			status_t ret;
			kill_thread(tid);
			wait_for_thread(tid,&ret);
			delete_sem(sem);
			delete_area(area);
			free(particle_list);
		}
		else
		{
			// set the color list black so that the screen doesn't seem
			// to freeze while saving the framebuffer
			rgb_color c={0,0,0,255};
			rgb_color palette[256];
			// build the palette
			for(int i=0;i<256;i++)
				palette[i] = c;
			// set the palette
			SetColorList(palette);
			// save the framebuffer
			for(int y=0;y<480;y++)
				memcpy(save_buffer+640*y,frame_buffer+y*line_length,640);
		}
	}
}


int32
NWindowScreen::Entry(void* p)
{
   return ((NWindowScreen*)p)->MyCode();
}


int32
NWindowScreen::MyCode()
{
	bigtime_t trgt = system_time() + frame_pause;
	srandom(system_time());
	// beforehand stuff
	particle_count = 1024*2;
	particle_list = (particle *)malloc(sizeof(particle)*particle_count);
	for (uint32 i=0; i<particle_count; i++) {
		uint32 rand_max = 0xffffffff;		
		particle_list[i].init_velocity = -((double)((rand_max>>1)+(random()%(rand_max>>1)))/rand_max)*3.333; // magic number
		particle_list[i].gravity = -(((double)((rand_max>>1)+(random()%(rand_max>>1)))/rand_max))*0.599; // more magic
		
		// make the particle initialy invisible and fixed, but at a random moment in time
		particle_list[i].lx = 0;
		particle_list[i].ly = 0;
		particle_list[i].last_draw = INVALID;
		particle_list[i].timeval = random() & 64;
		particle_list[i].x = 0; // this gets figured out at drawtime
		particle_list[i].y = 0; // same here
		particle_list[i].cos_z_theta = cos(random() % 360); // grab an angle
	}
	
	
	// the loop o' fun
	while (!(((NApplication*)be_app)->is_quitting)) {
		// try to sync with the vertical retrace
		if (BScreen(this).WaitForRetrace() != B_OK) {
			// snoze for a bit so that other threads can be happy. 
			// We are realtime priority you know
			if (system_time() < trgt)
				snooze(trgt - system_time());
			trgt = system_time() + frame_pause;
		}
		
		// gain access to the framebuffer before writing to it.
		acquire_sem(sem); // block until we're allowed to own the framebuffer
		
		///////////////////////////////
		// do neat stuff here //
		//////////////////////////////
		PRINT(("rendering a frame.\n"));
	
		
		// eye candy VII - particles! - my own cookin
		int32 x, y, cx,cy;
		set_frame_rate(60.); // woo. ntsc
		// calculate the center
		cx = width/2;
		cy = height/2;
		
		// palette test
		//set_frame_rate(0.1);
		//for(int i=0;i<256;i++)
		//	draw_line(i,0,i,height, i);
		
		PRINT(("Starting particle drawing loop\n"));
		particle *s = particle_list;
		for (uint32 i=0; i<particle_count; i++) {
			PRINT(("drawing particle %d\r", i));
			
			// save the old position
			s->lx = s->x;
			s->ly = s->y;
			
			PRINT(("cx=%d, cy=%d\n", cx,cy));
							
			// move the particle			
			// find y and x
			// (s->gravity/2)*(s->timeval*s->timeval) * 1.85 is magic
			y = s->y = (int32)(cy + (int32)((s->gravity/2)*(s->timeval*s->timeval)*1.94) + ((s->init_velocity - (s->gravity*s->timeval)) * s->timeval));
			x = s->x = (int32)(cx + (int32)(s->timeval * s->cos_z_theta)); // 3d rotation
		
			// interate timeval
			s->timeval++;
		
			// sanity check
			if(x <= 0)
				goto erase_and_reset;
			if(x > width)
				goto erase_and_reset;
			if(y < 0)
				goto erase; // invisible + erase last position
			if(y > height)
				goto erase_and_reset;
					
			// erase the previous position, if necessary
			if (s->last_draw != INVALID)
				set_pixel(s->lx,s->ly,0);
			
			// if it's visible, then draw it.
			set_pixel(s->x,s->y, 169);
			s->last_draw = 1;
			goto loop;
			
			erase_and_reset:
			if((s->lx <= width) && (s->lx >= 0) && (s->ly <= height) && (s->ly >= 0))
				set_pixel(s->lx, s->ly,0);
			s->x = 0;
			s->y = 0;
			s->lx = 0;
			s->ly = 0;
			s->timeval = 0;
			s->last_draw = INVALID;
			goto loop;
			
			erase:
			// erase it.
			if(s->last_draw != INVALID)
				set_pixel(s->lx, s->ly,0);
			s->lx = s->x;
			s->ly = s->y;
			s->last_draw = INVALID;
			loop:
				s++;
			//printf("end draw loop\n");
		}
		PRINT(("frame done\n"));
		
		//////////////////////////////////
		// stop doing neat stuff //
		/////////////////////////////////
		
		// release the semaphore while waiting. gotta release it
		// at some point or nasty things will happen!
		release_sem(sem);
		// loop for another frame!
	}
	return B_OK;
}

//////////////////////////////
// Misc - a place for demos to put their convenience functions
//////////////////////////////


//////////////////////////////
// My Silly Raster Lib 
//////////////////////////////

/*

	Functions:
void draw_line(int x1, int y1, int x2, int y2, int color);
void draw_rect(int x, int y, int w, int h, int color);
void fill_rect(int x, int y, int w, int h, int color);
void draw_ellipse(int x, int y, int xradius, int yradius, int color);
void fill_ellipse(int x, int y, int xradius, int yradius, int color);

*/

void
NWindowScreen::draw_line(int x1, int y1, int x2, int y2, int color)
{
	// Simple Bresenham's line drawing algorithm
	int d,x,y,ax,ay,sx,sy,dx,dy;
	
#define ABS(x) (((x)<0) ? -(x) : (x))
#define SGN(x) (((x)<0) ? -1 : 1)

	dx=x2-x1; ax=ABS(dx)<<1; sx=SGN(dx);
	dy=y2-y1; ay=ABS(dy)<<1; sy=SGN(dy);
	
	x=x1;
	y=y1;
	if(ax>ay)
	{
		d=ay-(ax>>1);
		for(;;)
		{
			set_pixel(x,y,color);
			if(x==x2) return;
			if(d>=0)
			{
				y+=sy;
				d-=ax;
			}
			x+=sx;
			d+=ay;
		}
	}
	else
	{
		d=ax-(ay>>1);
		for(;;)
		{
			set_pixel(x,y,color);
			if(y==y2) return;
			if(d>=0)
			{
				x+=sx;
				d-=ay;
			}
			y+=sy;
			d+=ax;
		}
	}
}


void
NWindowScreen::draw_rect(int x, int y, int w, int h, int color)
{
	draw_line(x,y,x+w,y,color);
	draw_line(x,y,x,y+h,color);
	draw_line(x,y+h,x+w,y+h,color);
	draw_line(x+w,y,x+w,y+h,color);
}


void
NWindowScreen::fill_rect(int x, int y, int w, int h, int color)
{
	for (int i = 0; i < w; i++) {
		for (int j = 0; j < h; j++) {
			set_pixel(i, j, color);
		}
	}
}


void
NWindowScreen::draw_ellipse(int cx, int cy, int wide, int deep, int color)
{
	// if we're asked to draw a really small ellipse, put a single pixel in the buffer
	// and bail
	if((wide < 1) || (deep < 1))
	{
		set_pixel(cx,cy,color);
		return;
	}
	
	// MidPoint Ellipse algorithm. 
	// page 90 of Computer Graphics Principles and Practice 2nd edition (I highly recommend this book)
	int16 x, y;
	int16 wide_squared, deep_squared;
	double d;

	x = 0;	
	y = deep;
	wide_squared = wide * wide;
	deep_squared = deep * deep;
	d = deep_squared - (wide_squared*deep) + (wide_squared/4);
	
	ellipse_points(x, y, cx, cy, color);
	while((wide_squared*(y - 0.5)) > (deep_squared*(x + 1)))
	{
		if(d < 0)
			d += deep_squared*(2*x + 3);
		else
		{
			d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2);
			y--;
		}
		x++;
		ellipse_points(x, y, cx, cy, color);
	}
	
	d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared;
	while(y > 0)
	{
		if(d < 0)
		{
			d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3);
			x++;
		}
		else
			d += wide_squared*(-2*y + 3);
		y--;
		ellipse_points(x, y, cx, cy, color);
	}
}


void
NWindowScreen::fill_ellipse(int cx, int cy, int wide, int deep, int color)
{
	// if we're asked to draw a really small ellipse, put a single pixel in the buffer
	// and bail
	if((wide < 1) || (deep < 1))
	{
		set_pixel(cx,cy,color);
		return;
	}
	
	// MidPoint Ellipse algorithm. 
	// page 90 of Computer Graphics Principles and Practice 2nd edition (I highly recommend this book)
	int16 x, y;
	int16 wide_squared, deep_squared;
	double d;

	x = 0;	
	y = deep;
	wide_squared = wide * wide;
	deep_squared = deep * deep;
	d = deep_squared - (wide_squared*deep) + (wide_squared/4);
	
	ellipse_fill_points(x, y, cx, cy, color);
	while((wide_squared*(y - 0.5)) > (deep_squared*(x + 1)))
	{
		if(d < 0)
			d += deep_squared*(2*x + 3);
		else
		{
			d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2);
			y--;
		}
		x++;
		ellipse_fill_points(x, y, cx, cy, color);
	}
	
	d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared;
	while(y > 0)
	{
		if(d < 0)
		{
			d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3);
			x++;
		}
		else
			d += wide_squared*(-2*y + 3);
		y--;
		ellipse_fill_points(x, y, cx, cy, color);
	}
}


void
NWindowScreen::ellipse_points(int x, int y, int x_offset, int y_offset, int color)
{
	// fill four pixels for every iteration in draw_ellipse
	
	// the x_offset and y_offset values are needed since the midpoint ellipse algorithm 
	// assumes the midpoint to be at the origin
	// do a sanity check before each set_pixel, that way we clip to the edges
	
	int xCoord, yCoord;

	xCoord = x_offset + x;
	yCoord = y_offset + y;
	if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
		set_pixel(xCoord,yCoord,color);
	xCoord = x_offset - x;
	if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
		set_pixel(xCoord,yCoord,color);
	xCoord = x_offset + x;
	yCoord = y_offset - y;
	if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
		set_pixel(xCoord,yCoord,color);
	xCoord = x_offset - x;
	if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
		set_pixel(xCoord,yCoord,color);
}


void
NWindowScreen::ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color)
{
	// put lines between two pixels twice. once for y positive, the other for y negative (symmetry)
	// for every iteration in fill_ellipse
	
	// the x_offset and y_offset values are needed since the midpoint ellipse algorithm 
	// assumes the midpoint to be at the origin
	// do a sanity check before each set_pixel, that way we clip to the edges
	
	int xCoord1, yCoord1;
	int xCoord2, yCoord2;

	xCoord1 = x_offset - x;
	yCoord1 = y_offset + y;
	xCoord2 = x_offset + x;
	yCoord2 = y_offset + y;
	if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height))	
		if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height))
			draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color);
	
	xCoord1 = x_offset - x;
	yCoord1 = y_offset - y;
	xCoord2 = x_offset + x;
	yCoord2 = y_offset - y;
	if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height))	
		if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height))
			draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color);

}