<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://dreamcast.wiki/wiki/index.php?action=history&amp;feed=atom&amp;title=2D_Rendering_Without_PVR</id>
	<title>2D Rendering Without PVR - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://dreamcast.wiki/wiki/index.php?action=history&amp;feed=atom&amp;title=2D_Rendering_Without_PVR"/>
	<link rel="alternate" type="text/html" href="https://dreamcast.wiki/wiki/index.php?title=2D_Rendering_Without_PVR&amp;action=history"/>
	<updated>2026-05-12T20:35:43Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.39.3</generator>
	<entry>
		<id>https://dreamcast.wiki/wiki/index.php?title=2D_Rendering_Without_PVR&amp;diff=2136&amp;oldid=prev</id>
		<title>Unknown user: Created page with &quot;&lt;b&gt;By [mailto:obsidianglow@NOSPAMhotpop.com BlackAura]&lt;/b&gt;  		  &lt;h1&gt;The basics&lt;/h1&gt;    		&lt;h2&gt;Setting up a video mode&lt;/h2&gt; 		&lt;p&gt;The first thing you should do when doing any kin...&quot;</title>
		<link rel="alternate" type="text/html" href="https://dreamcast.wiki/wiki/index.php?title=2D_Rendering_Without_PVR&amp;diff=2136&amp;oldid=prev"/>
		<updated>2023-03-02T01:05:43Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;&amp;lt;b&amp;gt;By [mailto:obsidianglow@NOSPAMhotpop.com BlackAura]&amp;lt;/b&amp;gt;  		  &amp;lt;h1&amp;gt;The basics&amp;lt;/h1&amp;gt;    		&amp;lt;h2&amp;gt;Setting up a video mode&amp;lt;/h2&amp;gt; 		&amp;lt;p&amp;gt;The first thing you should do when doing any kin...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;b&amp;gt;By [mailto:obsidianglow@NOSPAMhotpop.com BlackAura]&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h1&amp;gt;The basics&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Setting up a video mode&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;The first thing you should do when doing any kind of&lt;br /&gt;
		graphics programming is set up a video mode. On the&lt;br /&gt;
		Dreamcast, this isn&amp;#039;t really that important - KallistiOS&lt;br /&gt;
		will automatically set the video hardware up into 640x480,&lt;br /&gt;
		16 bit 60Hz mode. However, it&amp;#039;s still a good idea to set&lt;br /&gt;
		the video mode manually, in case they change something.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;This couldn&amp;#039;t really be much simpler - we just need to&lt;br /&gt;
		call the function vid_set_mode, giving it our desired&lt;br /&gt;
		resolution and colour mode, and it&amp;#039;ll do the rest:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;pre&amp;gt;vid_set_mode(display_mode, pixel_mode);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;display_mode can be one of:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;table cols=&amp;quot;4&amp;quot; rows=&amp;quot;11&amp;quot; border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;display_mode&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Width&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Height&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Refresh rate&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_320x240&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;320&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;240&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;60Hz, VGA&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_640x480&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;640&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;480&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;60Hz, VGA&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_256x256&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;256&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;256&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;60Hz&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_768x480&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;768&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;480&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;60Hz&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_768x576&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;768&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;576&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;60Hz&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_640x480_PAL_IL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;640&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;480&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;50Hz&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_256x256_PAL_IL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;256&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;256&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;50Hz&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_768x480_PAL_IL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;768&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;480&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;50Hz&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_768x576_PAL_IL&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;768&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;576&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;50Hz&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;DM_800x608&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;800&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;608&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;VGA Only&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
		&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;The 60Hz modes will always be 60Hz, even on a PAL Dreamcast.&lt;br /&gt;
		The 50Hz modes will always be 50Hz, even on an NTSC Dreamcast.&lt;br /&gt;
		While most PAL TVs can display a 60Hz signal, some can not,&lt;br /&gt;
		and virtually no NTSC TVs can display a 50Hz signal. It&amp;#039;s&lt;br /&gt;
		usually safe to use the 60Hz modes, but you might want to put&lt;br /&gt;
		in an option to use 50Hz on PAL Dreamcasts for those European&lt;br /&gt;
		gamers with old TVs. I&amp;#039;ll go into more detail on that later.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;One rather strange omission that is worth taking note of -&lt;br /&gt;
		there is no 320x240 50Hz mode. I don&amp;#039;t know why this is, because&lt;br /&gt;
		it&amp;#039;s certainly not too difficult to do - it was just never&lt;br /&gt;
		included in KOS. If anyone really needs it, there is a patch to&lt;br /&gt;
		add that mode (DM_320x240_PAL) to KOS 1.2.0, and it should be&lt;br /&gt;
		included in KOS 1.2.1&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;pixel_mode can be one of:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;table cols=&amp;quot;4&amp;quot; rows=&amp;quot;11&amp;quot; border=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;pixel_mode&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Pixel size&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Colour format&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;PM_RGB555&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;15 bit&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;0RRRRRGGGGGBBBBB&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;PM_RGB565&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;16 bit&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RRRRRGGGGGGBBBBB&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
			&amp;lt;tr&amp;gt;&lt;br /&gt;
				&amp;lt;td&amp;gt;PM_RGB888&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;24 bit&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;RRRRRRRR GGGGGGGG BBBBBBBB&amp;lt;/td&amp;gt;&lt;br /&gt;
			&amp;lt;/tr&amp;gt;&lt;br /&gt;
		&amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;The best mode to use on the Dreamcast is RGB565 - it provides&lt;br /&gt;
		fairly good colour quality, it&amp;#039;s significantly faster than 24-bit&lt;br /&gt;
		mode, and it uses less memory. Just trust me on this - it&amp;#039;s the&lt;br /&gt;
		best mode to use for pretty much everything that we&amp;#039;re going to&lt;br /&gt;
		be doing.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;So, to initialise the screen to 640x480 60Hz, in RGB565 colour&lt;br /&gt;
		mode, we&amp;#039;d use the following line:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;pre&amp;gt;vid_set_mode(DM_640x480, PM_RGB565);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;RGB565 colour mode&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;In RGB565 mode, the three colour components are packed into two&lt;br /&gt;
		bytes. The upper 5 bits contain the red data, the next 6 contain&lt;br /&gt;
		the green data, and the last 5 contain the blue data. Red and blue&lt;br /&gt;
		range from 0 to 31, and green ranges from 0 to 63. The reason that&lt;br /&gt;
		green is given more space is because the human eye is more sensitive&lt;br /&gt;
		to green.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;Colours on a computer are typically represented using three bytes&lt;br /&gt;
		- one each for red, green and blue, ranging from 0 to 255. There&amp;#039;s an&lt;br /&gt;
		easy way to convert from this format to the 16-bit RGB565 format that&lt;br /&gt;
		we need - we just use a macro:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;pre&amp;gt;#define PACK_PIXEL(r, g, b) ( ((r &amp;amp;amp; 0xF8) &amp;amp;lt;&amp;amp;lt; 8) | ((g &amp;amp;amp; 0xFC) &amp;amp;lt;&amp;amp;lt; 3) | (b &amp;amp;gt;&amp;amp;gt; 3) )&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;That macro uses two AND operations to mask out the unnecessary bits&lt;br /&gt;
		of the red and green components, bit shifts the values to get them&lt;br /&gt;
		into the correct place, and ORs them together. For some strange reason,&lt;br /&gt;
		that macro isn&amp;#039;t included anywhere in KOS.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Drawing a single pixel&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;No matter how complex the graphics we&amp;#039;re trying to make, no matter&lt;br /&gt;
		how many complex formulae and techniques we&amp;#039;re using, no matter how&lt;br /&gt;
		long it&amp;#039;s taken to draw our artwork, ALL 2D graphics come down to this&lt;br /&gt;
		- drawing a single pixel. For many things, where we need to plot&lt;br /&gt;
		multiple pixels at once, we can modify this routime to make it faster,&lt;br /&gt;
		but it&amp;#039;ll still just be a variation on this. So how exactly do we draw&lt;br /&gt;
		a single pixel?&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;On the Dreamcast, the image currently being displayed on the screen&lt;br /&gt;
		is stored in an area of video memory called the framebuffer. The pixels&lt;br /&gt;
		are stored in order, from left to right, then from top to bottom, each&lt;br /&gt;
		as a single 16-bit value. So in order to find the location in the&lt;br /&gt;
		framebuffer of a single pixel, we can use this formula:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;pre&amp;gt;location = x_coordinate + (y_coordinate * width)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;So, assuming a resolution of 640x480, we can do this:&amp;lt;/p&amp;gt;&lt;br /&gt;
		&lt;br /&gt;
		&amp;lt;pre&amp;gt;location = x + (y * 640)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;Now all we need to know is where the framebuffer is. KOS provides us&lt;br /&gt;
		with a pointer to this location, named vram_s. So all we need to do the&lt;br /&gt;
		get the location in memory of a single pixel on the screen is:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;pre&amp;gt;vram_s[x + (y * 640)]&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;From there, it should be easy to see how to draw a single pixel - we&lt;br /&gt;
		just need to set the value of that location in memory to the colour we&lt;br /&gt;
		want the pixel to be:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;pre&amp;gt;vram_s[x + (y * 640)] = PACK_PIXEL(r, g, b);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt;Clipping&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;Now that all works fine, until we come across one additional problem -&lt;br /&gt;
		clipping. What happens if we were to try to write to negative X or Y&lt;br /&gt;
		coordinates, or write off the bottom or the right of the screen? We&amp;#039;d&lt;br /&gt;
		be writing either to the wrong part of the screen, or an area of memory&lt;br /&gt;
		that we aren&amp;#039;t allowed to write to. We could cause all kinds of problems.&lt;br /&gt;
		So we just don&amp;#039;t do it - we have to add some checking to make sure we&lt;br /&gt;
		aren&amp;#039;t trying to draw outside the screen:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;pre&amp;gt;if((x &amp;amp;gt;= 0) &amp;amp;amp;&amp;amp;amp; (x &amp;amp;lt; 640) &amp;amp;amp;&amp;amp;amp; (y &amp;amp;gt;= 0) &amp;amp;amp;&amp;amp;amp; (y &amp;amp;lt; 480))&lt;br /&gt;
	vram_s[x + (y * 640)] = PACK_PIXEL(r, g, b);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;Now we have a safe, effective way to draw a single pixel on the screen.&lt;br /&gt;
		Of course, it&amp;#039;d be more convenient as a macro:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#define DRAW_PIXEL(x, y, c) \&lt;br /&gt;
	if((x &amp;amp;gt;= 0) &amp;amp;amp;&amp;amp;amp; (x &amp;amp;lt; 640) &amp;amp;amp;&amp;amp;amp; (y &amp;amp;gt;= 0) &amp;amp;amp;&amp;amp;amp; (y &amp;amp;lt; 480)) \&lt;br /&gt;
		vram_s[x + (y * 640)] = c;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;Now, to draw a single pixel, we can just work out the colour we want to set it to using PACK_PIXEL, and the use DRAW_PIXEL to draw it.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt;Example&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;The example program for this tutorial ([http://files.frashii.com/~sp00nz/Doom/files/BlackAura/tutes/2dgfx/example3.zip example1.zip]),&lt;br /&gt;
		draws pixels randomly to the screen, using PACK_PIXEL and DRAW_PIXEL. See if you&lt;br /&gt;
		can make it do something more interesting.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;h1&amp;gt;Horizontal and vertical lines&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Horizontal lines&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;Drawing pixels is probably getting kinda boring by now, so let&amp;#039;s&lt;br /&gt;
		draw something more interesting. As a start, we&amp;#039;ll draw a simple&lt;br /&gt;
		horizontal line, which is about the simplest shape you can do. A&lt;br /&gt;
		line is basically a load of pixels, arranged in a line. So they&lt;br /&gt;
		shouldn&amp;#039;t be that hard to draw - we just need to draw a load of&lt;br /&gt;
		pixels in a line:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_hline(int x1, int x2, int y, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	int cx;&lt;br /&gt;
&lt;br /&gt;
	// Draw the line&lt;br /&gt;
	for(cx = x1; cx &amp;amp;lt;= x2; cx++)&lt;br /&gt;
		DRAW_PIXEL(cx, y, c);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;One problem there - what if x2 is smaller than x1? The line won&amp;#039;t&lt;br /&gt;
		be drawn. So, if x2 is smaller than x1, we should swap them around:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_hline(int x1, int x2, int y, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	int cx;&lt;br /&gt;
&lt;br /&gt;
	// Swap x1 and x2 if necessary&lt;br /&gt;
	if(x1 &amp;amp;gt; x2)&lt;br /&gt;
	{&lt;br /&gt;
		cx = x1;&lt;br /&gt;
		x1 = x2;&lt;br /&gt;
		x2 = cx;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Draw the line&lt;br /&gt;
	for(cx = x1; cx &amp;amp;lt;= x2; cx++)&lt;br /&gt;
		DRAW_PIXEL(cx, y, c);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;And that&amp;#039;s it... Or is it? The more observant of you may notice that&lt;br /&gt;
		this code is actually very bloated. Remember that DRAW_PIXEL is checking&lt;br /&gt;
		if the pixel is on the screen for every pixel we draw. But, since this&lt;br /&gt;
		is just a horizontal line, we know that if both points are on the screen,&lt;br /&gt;
		the whole line is on the screen. If the line&amp;#039;s off the screen entirely,&lt;br /&gt;
		we can just ignore it. If the line is on the screen, we can tell how much&lt;br /&gt;
		of the line is on the screen, and adjust the left and right coordinates&lt;br /&gt;
		appropriately. This is more or less the same thing as the clipping that&lt;br /&gt;
		DRAW_PIXEL does, but we only need to do it for the line, not each pixel:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_hline(int x1, int x2, int y, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	int cx;&lt;br /&gt;
&lt;br /&gt;
	// Swap x1 and x2 if necessary&lt;br /&gt;
	if(x1 &amp;amp;gt; x2)&lt;br /&gt;
	{&lt;br /&gt;
		cx = x1;&lt;br /&gt;
		x1 = x2;&lt;br /&gt;
		x2 = cx;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Don&amp;#039;t bother if the line&amp;#039;s off the screen&lt;br /&gt;
	if( (y &amp;amp;lt; 0) || (y &amp;amp;gt; 479) || (x1 &amp;amp;gt; 639) || (x2 &amp;amp;lt; 0) )&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Clip the line to the edge of the screen&lt;br /&gt;
	if(x1 &amp;amp;lt; 0)&lt;br /&gt;
		x1 = 0;&lt;br /&gt;
	if(x2 &amp;amp;gt; 639)&lt;br /&gt;
		x2 = 639;&lt;br /&gt;
&lt;br /&gt;
	// Draw the line&lt;br /&gt;
	for(cx = x1; cx &amp;amp;lt;= x2; cx++)&lt;br /&gt;
		vram_s[cx + (y*640)] = c;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;That&amp;#039;s a bit better, but there&amp;#039;s still one problem - for each pixel,&lt;br /&gt;
		we&amp;#039;re doing y*640, which will be the same for each and every pixel. In&lt;br /&gt;
		face, since we&amp;#039;re drawing from left to right, each pixel will be next&lt;br /&gt;
		to the previous pixel. So we can do something like this:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_hline(int x1, int x2, int y, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	int cx;&lt;br /&gt;
	uint16 *line_buffer;&lt;br /&gt;
&lt;br /&gt;
	// Swap x1 and x2 if necessary&lt;br /&gt;
	if(x1 &amp;amp;gt; x2)&lt;br /&gt;
	{&lt;br /&gt;
		cx = x1;&lt;br /&gt;
		x1 = x2;&lt;br /&gt;
		x2 = cx;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Don&amp;#039;t bother if the line&amp;#039;s off the screen&lt;br /&gt;
	if( (y &amp;amp;lt; 0) || (y &amp;amp;gt; 479) || (x1 &amp;amp;gt; 639) || (x2 &amp;amp;lt; 0) )&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Clip the line to the edge of the screen&lt;br /&gt;
	if(x1 &amp;amp;lt; 0)&lt;br /&gt;
		x1 = 0;&lt;br /&gt;
	if(x2 &amp;amp;gt; 639)&lt;br /&gt;
		x2 = 639;&lt;br /&gt;
&lt;br /&gt;
	// Set line_buffer to the beginning of the line&lt;br /&gt;
	line_buffer = vram_s + x1 + (y * 640);&lt;br /&gt;
&lt;br /&gt;
	// Draw the line&lt;br /&gt;
	for(cx = x1; cx &amp;amp;lt;= x2; cx++)&lt;br /&gt;
		*line_buffer++ = c;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;That probably won&amp;#039;t make a lot of sense unless you know something&lt;br /&gt;
		about pointers in C - line_buffer is set to the location of the first&lt;br /&gt;
		pixel of the line we&amp;#039;re drawing. We then draw the pixel, and increment&lt;br /&gt;
		line_buffer by one pixel, so it now points to the location of the next&lt;br /&gt;
		pixel.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;A horizontal line is actually far more useful than it would first&lt;br /&gt;
		appear. A horizontal line is very useful for drawing filled shapes -&lt;br /&gt;
		you just draw a horizontal line from the left-most point of the shape&lt;br /&gt;
		on a given line, to the right-most point of the shape. Then you repeat&lt;br /&gt;
		that for each line. That&amp;#039;s how most software rendered 3D games fill&lt;br /&gt;
		polygons.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Vertical lines&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;These are pretty much exactly the same as horizontal lines. However,&lt;br /&gt;
		we&amp;#039;re now drawing down instead of right. Basically, we swap the X and&lt;br /&gt;
		Y coordinates around, and we skip 640 pixels instead of 1 - we jump&lt;br /&gt;
		down one pixel instead of right one pixel:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_vline(int x, int y1, int y2, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	int cy;&lt;br /&gt;
	uint16 *line_buffer;&lt;br /&gt;
&lt;br /&gt;
	// Swap y1 and y2 if necessary&lt;br /&gt;
	if(y1 &amp;amp;gt; y2)&lt;br /&gt;
	{&lt;br /&gt;
		cy = y1;&lt;br /&gt;
		y1 = y2;&lt;br /&gt;
		y2 = cy;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Don&amp;#039;t bother if the line&amp;#039;s off the screen&lt;br /&gt;
	if( (x &amp;amp;lt; 0) || (x &amp;amp;gt; 639) || (y1 &amp;amp;gt; 479) || (y2 &amp;amp;lt; 0) )&lt;br /&gt;
		return;&lt;br /&gt;
&lt;br /&gt;
	// Clip the line to the edge of the screen&lt;br /&gt;
	if(y1 &amp;amp;lt; 0)&lt;br /&gt;
		y1 = 0;&lt;br /&gt;
	if(y2 &amp;amp;gt; 479)&lt;br /&gt;
		y2 = 479;&lt;br /&gt;
&lt;br /&gt;
	// Set line_buffer to the beginning of the line&lt;br /&gt;
	line_buffer = vram_s + x + (y1 * 640);&lt;br /&gt;
&lt;br /&gt;
	// Draw the line&lt;br /&gt;
	for(cy = y1; cy &amp;amp;lt;= y2; cy++)&lt;br /&gt;
	{&lt;br /&gt;
		*line_buffer = c;&lt;br /&gt;
		line_buffer += 640;&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;If you can just adapt something you already have to perform a new&lt;br /&gt;
		function, it&amp;#039;s usually a good idea to try it. Trivial modifications to&lt;br /&gt;
		code that you know works are usually easier to get working than writing&lt;br /&gt;
		the	whole thing from scratch.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Hollow boxes&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;Now that we have the code to draw horizontal and vertical lines,&lt;br /&gt;
		drawing a hollow box should be very easy - we just draw two horizontal&lt;br /&gt;
		lines, and two vertical lines:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_hbox(int x1, int y1, int x2, int y2, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	draw_hline(x1, x2, y1, c);&lt;br /&gt;
	draw_hline(x1, x2, y2, c);&lt;br /&gt;
	draw_vline(x1, y1, y2, c);&lt;br /&gt;
	draw_vline(x2, y1, y2, c);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Filled boxes&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;All we do here is draw a series of horizontal lines, which will fill&lt;br /&gt;
		an area of the screen. We know what the left and right coordinates of&lt;br /&gt;
		the box are on each scanline, and we know how far up and down the box&lt;br /&gt;
		goes. So we can just do:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_fbox(int x1, int y1, int x2, int y2, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	int cy;&lt;br /&gt;
	for(cy = y1; cy &amp;amp;lt;= y2; cy++)&lt;br /&gt;
		draw_hline(x1, x2, cy, c);&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Example&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;The example program for this tutorial ([http://files.frashii.com/~sp00nz/Doom/files/BlackAura/tutes/2dgfx/example2.zip example2.zip]),&lt;br /&gt;
		draws coloured gradients using the draw_hline and draw_vline&lt;br /&gt;
		functions, then draws a pattern using the filled and unfilled&lt;br /&gt;
		box functions. The video code is contained in a separate file&lt;br /&gt;
		which you can use in your own programs. Just copy video.c and&lt;br /&gt;
		video.h, put them into your own program, and #include video.h&lt;br /&gt;
		when you want to use the video functions. All future examples&lt;br /&gt;
		will be set up this way.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h1&amp;gt;General lines&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
		&amp;lt;h2&amp;gt;Lines in any direction&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;So far, we&amp;#039;ve just drawn horizontal and vertical lines. They were&lt;br /&gt;
		easy, but they&amp;#039;re also quite boring. So now we&amp;#039;re going to draw lines&lt;br /&gt;
		that can go in any direction we want. Assuming you know some farily&lt;br /&gt;
		basic line geometry, drawing a line is pretty easy. However, the simple&lt;br /&gt;
		way is also very slow, which is where things start to get interesting.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;There are a multitude of ways to draw lines, ranging from the&lt;br /&gt;
		obvious to the utterly insane. The method I&amp;#039;m describing here is the&lt;br /&gt;
		most common, which was developed by Jack Bressenham at IBM. It&amp;#039;s not&lt;br /&gt;
		the best, but it&amp;#039;s comprehensible at least, and can be fairly easily&lt;br /&gt;
		derived.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;This kinda assumes that you know some basic line geometry, such as&lt;br /&gt;
		how to find the gradient (slope) of a line, and some other simple&lt;br /&gt;
		things (such as what the gradient actually is, and what you can do with&lt;br /&gt;
		it). I did this kind of stuff at school when I was about twelve or&lt;br /&gt;
		thirteen. If you didn&amp;#039;t learn any of this, can&amp;#039;t remember it, or fell&lt;br /&gt;
		asleep in class, you&amp;#039;re going to find this a little difficult. That&amp;#039;s&lt;br /&gt;
		what you get for not paying attention in math class.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Line geometry&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;In order to draw a line, we must first be able to define a line.&lt;br /&gt;
		Once we can develop a mathematical model of a line, we can try to tell&lt;br /&gt;
		a computer how to draw one.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;In general, and line can be defined by two values - it&amp;#039;s gradient,&lt;br /&gt;
		and it&amp;#039;s y-intercept. The gradient tells us how steep the line is, and&lt;br /&gt;
		the y-intercept tells us where the line intersects with the Y axis. In&lt;br /&gt;
		practical terms, we also need two end points, which simply define the&lt;br /&gt;
		limits of the line in the X axis. The Y coordinates of each end point&lt;br /&gt;
		can be calculated using the Y-intercept and the gradient, and therefore&lt;br /&gt;
		the gradient and Y-intercept can be calculated from the end points.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;The general equation for a line is...&amp;lt;/p&amp;gt;&lt;br /&gt;
		&amp;lt;pre&amp;gt;y = mx + b&amp;lt;/pre&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;...where &amp;quot;x&amp;quot; and &amp;quot;y&amp;quot; are the coordinates of each point on the line,&lt;br /&gt;
		m is the gradient, and b is the y-intercept. Therefore, if we know the&lt;br /&gt;
		X coordinate of a point, we can find it&amp;#039;s Y coordinate by plugging it&lt;br /&gt;
		into the above formula. We can use this to find the Y coordinate of any&lt;br /&gt;
		point between our two end points.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;Finally, we need to know how to calculate the Y intercept and&lt;br /&gt;
		gradient, assuming that we know the coordinates of two points on the&lt;br /&gt;
		line - the end points in our case. First, we calculate the gradient&lt;br /&gt;
		by dividing the Y displacement (Y2 - Y1) by the X displacement&lt;br /&gt;
		(X2 - X1). We can then use a slightly modified version of the first&lt;br /&gt;
		equation to find the y-intercept.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;A first attempt&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;Now, that sounds easy (yeah, right), so let&amp;#039;s put it into&lt;br /&gt;
		practice...&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_line(int x1, int y1, int x2, int y2, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	float m, b;&lt;br /&gt;
	int dx = x2 - x1;&lt;br /&gt;
	int dy = y2 - y1;&lt;br /&gt;
&lt;br /&gt;
	// Calculate gradient and y-intercept&lt;br /&gt;
	m = (float)dy / (float)dx;&lt;br /&gt;
	b = y1 - m*x1;&lt;br /&gt;
&lt;br /&gt;
	// Work out which way we&amp;#039;re going - left or right&lt;br /&gt;
	dx = (x2 &amp;amp;gt; x1) ? 1 : -1;&lt;br /&gt;
&lt;br /&gt;
	// Draw start pixel&lt;br /&gt;
	DRAW_PIXEL(x1, y1, c);&lt;br /&gt;
&lt;br /&gt;
	// Draw the line&lt;br /&gt;
	while (x1 != x2)&lt;br /&gt;
	{&lt;br /&gt;
		x1 += dx;&lt;br /&gt;
		y1 = (int)(m*x1+b);&lt;br /&gt;
		DRAW_PIXEL(x1, y1, c);&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;There&amp;#039;s just one problem with that. If you try to draw some lines&lt;br /&gt;
		with it, you&amp;#039;ll notice that it doesn&amp;#039;t work for steep lines. We&amp;#039;re&lt;br /&gt;
		moving from left to right, and plotting one pixel at the appropriate&lt;br /&gt;
		height. But, for steep lines, we&amp;#039;ll just get a load of dots.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;A working attempt&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;So how do we fix it? It works fine for any lines which are shallow,&lt;br /&gt;
		so we can keep that. All we have to do for steep lines is reverse the&lt;br /&gt;
		X and Y coordinates - we flip all X and Ys in the equations above, and&lt;br /&gt;
		move from top to bottom, calculate an X coordinate, and plot one pixel&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_line(int x1, int y1, int x2, int y2, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	float m, b;&lt;br /&gt;
	int dx = x2 - x1;&lt;br /&gt;
	int dy = y2 - y1;&lt;br /&gt;
&lt;br /&gt;
	// Draw start pixel&lt;br /&gt;
	DRAW_PIXEL(x1, y1, c);&lt;br /&gt;
&lt;br /&gt;
	if(abs(dx) &amp;amp;gt; abs(dy))&lt;br /&gt;
	{&lt;br /&gt;
		// Shallow lines&lt;br /&gt;
		m = (float)dy / (float)dx;&lt;br /&gt;
		b = y1 - m*x1;&lt;br /&gt;
		dx = (x2 &amp;amp;gt; x1) ? 1 : -1;&lt;br /&gt;
		while (x1 != x2)&lt;br /&gt;
		{&lt;br /&gt;
			x1 += dx;&lt;br /&gt;
			y1 = (int)(m*x1+b);&lt;br /&gt;
			DRAW_PIXEL(x1, y1, c);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		// Steep lines&lt;br /&gt;
		m = (float)dx / (float)dy;&lt;br /&gt;
		b = x1 - m*y1;&lt;br /&gt;
		dy = (dy &amp;amp;lt; 0) ? -1 : 1;&lt;br /&gt;
		while(y1 != y2)&lt;br /&gt;
		{&lt;br /&gt;
			y1 += dy;&lt;br /&gt;
			x1 = (int)(m*y1+b);&lt;br /&gt;
			DRAW_PIXEL(x1, y1, c);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Digital differential analyser&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;Remember I said that this method was slow? Can you see why? When it&lt;br /&gt;
		calculates the X or Y coordinates for each point, it must multiply two&lt;br /&gt;
		numbers together. Since multiplications are quite slow, this slows the&lt;br /&gt;
		whole thing down. We can get around this by using additions instead.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;Every time we do a multiplication, we&amp;#039;re multiplying the gradient by&lt;br /&gt;
		the next Y or X coordinate. The gradient is always constant, and the X&lt;br /&gt;
		or Y coordinates are always exactly one more or one less than they were&lt;br /&gt;
		before. That means that, instead of the multiplication, we can just add&lt;br /&gt;
		or subtract the gradient each time.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;This technique is an example of a digital differential analyzer,&lt;br /&gt;
		apparently. I have no idea what a DDA actually is, but I know that this&lt;br /&gt;
		is one. The same idea applies to some other things, but nothing that&amp;#039;s&lt;br /&gt;
		immediately relevant. So you may as well forget about it.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_line(int x1, int y1, int x2, int y2, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	float m;&lt;br /&gt;
	int dx = x2 - x1;&lt;br /&gt;
	int dy = y2 - y1;&lt;br /&gt;
	float t = 0.5;&lt;br /&gt;
&lt;br /&gt;
	// Draw start pixel&lt;br /&gt;
	DRAW_PIXEL(x1, y1, c);&lt;br /&gt;
&lt;br /&gt;
	// Shallow lines&lt;br /&gt;
	if(abs(dx) &amp;amp;gt; abs(dy))&lt;br /&gt;
	{&lt;br /&gt;
		// Shallow lines&lt;br /&gt;
		m = (float)dy / (float)dx;&lt;br /&gt;
		t += y1;&lt;br /&gt;
		dx = (x2 &amp;amp;gt; x1) ? 1 : -1;&lt;br /&gt;
		m *= dx;&lt;br /&gt;
		while (x1 != x2)&lt;br /&gt;
		{&lt;br /&gt;
			x1 += dx;&lt;br /&gt;
			t += m;&lt;br /&gt;
			DRAW_PIXEL(x1, t, c);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		// Steep lines&lt;br /&gt;
		m = (float)dx / (float)dy;&lt;br /&gt;
		t += x1;&lt;br /&gt;
		dy = (dy &amp;amp;lt; 0) ? -1 : 1;&lt;br /&gt;
		m *= dy;&lt;br /&gt;
		while(y1 != y2)&lt;br /&gt;
		{&lt;br /&gt;
			y1 += dy;&lt;br /&gt;
			t += m;&lt;br /&gt;
			DRAW_PIXEL(t, y1, c);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Bresenham&amp;#039;s algorithm&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;This is a refinement of the DDA line algorithm above. To be totally&lt;br /&gt;
		honest, I don&amp;#039;t know if this is any faster on the Dreamcast, because it&lt;br /&gt;
		makes some assumptions that aren&amp;#039;t necessarily true on the SH-4.&lt;br /&gt;
		Specifically, it assumes that floating point operations are slower than&lt;br /&gt;
		integer operations, and that you can&amp;#039;t compare two arbitrary numbers -&lt;br /&gt;
		you can only compare one number to zero. I don&amp;#039;t see how it could&lt;br /&gt;
		be any slower, so we might as well give it a try. If anyone can be&lt;br /&gt;
		bothered to benchmark these, tell me what results you get.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;Anyway, all this does is removes all floating point operations in&lt;br /&gt;
		favour on integer operations, makes all comparisons relative to zero,&lt;br /&gt;
		and removes the division used to calculate the gradient.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;void draw_line(int x1, int y1, int x2, int y2, uint16 c)&lt;br /&gt;
{&lt;br /&gt;
	int dy = y2 - y1;&lt;br /&gt;
	int dx = x2 - x1;&lt;br /&gt;
	int stepx, stepy;&lt;br /&gt;
&lt;br /&gt;
	if (dy &amp;amp;lt; 0) { dy = -dy; stepy = -1; } else { stepy = 1; }&lt;br /&gt;
	if (dx &amp;amp;lt; 0) { dx = -dx; stepx = -1; } else { stepx = 1; }&lt;br /&gt;
	dy &amp;amp;lt;&amp;amp;lt;= 1;&lt;br /&gt;
	dx &amp;amp;lt;&amp;amp;lt;= 1;&lt;br /&gt;
&lt;br /&gt;
	DRAW_PIXEL(x1, y1, c);&lt;br /&gt;
&lt;br /&gt;
	if (dx &amp;amp;gt; dy)&lt;br /&gt;
	{&lt;br /&gt;
		// Shallow lines&lt;br /&gt;
		int fraction = dy - (dx &amp;amp;gt;&amp;amp;gt; 1);&lt;br /&gt;
		while (x1 != x2)&lt;br /&gt;
		{&lt;br /&gt;
			if (fraction &amp;amp;gt;= 0)&lt;br /&gt;
			{&lt;br /&gt;
				y1 += stepy;&lt;br /&gt;
				fraction -= dx;&lt;br /&gt;
			}&lt;br /&gt;
			x1 += stepx;&lt;br /&gt;
			fraction += dy;&lt;br /&gt;
			DRAW_PIXEL(x1, y1, c);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		// Steep lines&lt;br /&gt;
		int fraction = dx - (dy &amp;amp;gt;&amp;amp;gt; 1);&lt;br /&gt;
		while (y1 != y2)&lt;br /&gt;
		{&lt;br /&gt;
			if (fraction &amp;amp;gt;= 0)&lt;br /&gt;
			{&lt;br /&gt;
				x1 += stepx;&lt;br /&gt;
				fraction -= dy;&lt;br /&gt;
			}&lt;br /&gt;
			y1 += stepy;&lt;br /&gt;
			fraction += dx;&lt;br /&gt;
			DRAW_PIXEL(x1, y1, c);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Making it faster&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;There&amp;#039;s one final thing we can do to make the line drawing function&lt;br /&gt;
		work faster. It still uses DRAW_PIXEL, which means we&amp;#039;re going clipping&lt;br /&gt;
		tests and a multiplication for each pixel.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;Removing the multiplications is actually quite easy. We keep a&lt;br /&gt;
		pointer to the pixel currently being drawn, and move that pointer&lt;br /&gt;
		around as we need to. The problem is that we can now draw lines outside&lt;br /&gt;
		the screen, which can cause all sorts of problems, along with graphical&lt;br /&gt;
		corruption. That means we&amp;#039;d have to add some clipping routines. There&lt;br /&gt;
		are about as many ways to clip a line as there are to draw a line, and&lt;br /&gt;
		they&amp;#039;re all a complete pain in the backside to explain or implement. So&lt;br /&gt;
		I&amp;#039;m not going to go through them now. Maybe later.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;p&amp;gt;If you need more speed, you can use a totally different line drawing&lt;br /&gt;
		algorithm, such as Xiaolin Wu&amp;#039;s 2-step algorithm, which is a rather&lt;br /&gt;
		novel approach to drawing lines. If you want to know more about how any&lt;br /&gt;
		of this stuff works, here&amp;#039;s some sites you should look at:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;ul&amp;gt;&lt;br /&gt;
			&amp;lt;li&amp;gt;&lt;br /&gt;
				[http://graphics.lcs.mit.edu/%7Emcmillan/comp136/Lecture6/Lines.html&amp;quot;&amp;gt;Line-Drawing Algorithms]&lt;br /&gt;
			&amp;lt;/li&amp;gt;&lt;br /&gt;
		&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		&amp;lt;h2&amp;gt;Example&amp;lt;/h2&amp;gt;&lt;br /&gt;
		&amp;lt;p&amp;gt;The example program for this tutorial ([http://files.frashii.com/~sp00nz/Doom/files/BlackAura/tutes/2dgfx/example3.zip example3.zip],&lt;br /&gt;
		uses the line function we just developed to draw patterns on the&lt;br /&gt;
		screen. It draws two arch shapes, and a weird spirograph-like&lt;br /&gt;
		shape in the middle of the screen.&amp;lt;/p&amp;gt;&lt;/div&gt;</summary>
		<author><name>Unknown user</name></author>
	</entry>
</feed>