Software Textured Triangle Rasterization

I’m having an issue I suspect has to do with the accuracy of a fixed-int, and that I’m not recalculating, I’m just stepping.

64x64 bitmap at 800x600 (more severe when rendered at a different ratio)

http://puu.sh/q535m/0fd7bfd7cb.png

100x75 (1/8th size of screen) bitmap at 800x600 (only affects second triangle, possibly has to do with rotation?)

http://puu.sh/q53M6/999bd1b04f.png

This is how I’m rendering it:

s.drawBitmap(rainbow, 0, 0, getWidth(),getHeight());

public void drawBitmap(Bitmap src, int x, int y, int w, int h) {
		drawBitmap(src, x, y, x + w, y + h, 0, 0, src.width, src.height);
	}

		public void drawBitmap(Bitmap src, int dx0, int dy0, int dx1, int dy1, int sx0, int sy0, int sx1, int sy1) {
		drawBitmapTriangle(src,
				dx0, dy0, sx0, sy0,	// x0, y0, u0, v0
				dx1, dy0, sx1, sy0,	// x1, y1, u1, v1
				dx0, dy1, sx0, sy1	// x2, y2, u2, v2
		);

		drawBitmapTriangle(src,
				dx1, dy1, sx1, sy1,
				dx1, dy0, sx1, sy0,
				dx0, dy1, sx0, sy1
		);
	}

	public void drawBitmapTriangle(Bitmap src, int x0, int y0, int u0, int v0, int x1, int y1, int u1, int v1, int x2, int y2, int u2, int v2) {
		// C above A
		if (y2 < y0) {
			int tmp = x0;
			x0 = x2;
			x2 = tmp;

			tmp = y0;
			y0 = y2;
			y2 = tmp;

			tmp = u0;
			u0 = u2;
			u2 = tmp;

			tmp = v0;
			v0 = v2;
			v2 = tmp;
		}

		// B above A
		if (y1 < y0) {
			int tmp = x0;
			x0 = x1;
			x1 = tmp;

			tmp = y0;
			y0 = y1;
			y1 = tmp;

			tmp = u0;
			u0 = u1;
			u1 = tmp;

			tmp = v0;
			v0 = v1;
			v1 = tmp;
		}

		// C above B
		if (y2 < y1) {
			int tmp = x1;
			x1 = x2;
			x2 = tmp;

			tmp = y1;
			y1 = y2;
			y2 = tmp;

			tmp = u1;
			u1 = u2;
			u2 = tmp;

			tmp = v1;
			v1 = v2;
			v2 = tmp;
		}

		// A is below our boundaries, so we know the entire triangle is not visible.
		if (y0 >= maxY) {
			return;
		}

		// Our slope values
		int mx0 = 0, mx1 = 0, mx2 = 0;
		int mu0 = 0, mu1 = 0, mu2 = 0;
		int mv0 = 0, mv1 = 0, mv2 = 0;

		// A to B
		if (y0 != y1) {
			int d = y1 - y0;
			mx0 = ((x1 - x0) << 16) / d;
			mu0 = ((u1 - u0) << 16) / d;
			mv0 = ((v1 - v0) << 16) / d;
		}

		// B to C
		if (y1 != y2) {
			int d = y2 - y1;
			mx1 = ((x2 - x1) << 16) / d;
			mu1 = ((u2 - u1) << 16) / d;
			mv1 = ((v2 - v1) << 16) / d;
		}

		// A to C
		if (y0 != y2) {
			int d = y2 - y0;
			mx2 = ((x2 - x0) << 16) / d;
			mu2 = ((u2 - u0) << 16) / d;
			mv2 = ((v2 - v0) << 16) / d;
		}

		// To 16.16 fixed
		x0 <<= 16;
		u0 <<= 16;
		v0 <<= 16;

		// Start C at B
		x2 = x1 << 16;
		u2 = u1 << 16;
		v2 = v1 << 16;

		// A (16.16)
		x1 = x0;
		u1 = u0;
		v1 = v0;

		// A is above the minimum boundary
		if (y0 < minY) {
			// how far we need to step vertically to get back into bounds
			int step = minY - y0;

			// Step A toward C
			x0 += mx2 * step;
			u0 += mu2 * step;
			v0 += mv2 * step;

			// Step A toward B
			x1 += mx0 * step;
			u1 += mu0 * step;
			v1 += mv0 * step;

			y0 = 0;
		}

		// B is above minimum boundary
		if (y1 < minY) {
			int step = minY - y1;

			// Step B toward C
			x2 += mx1 * step;
			u2 += mu1 * step;
			v2 += mv1 * step;

			y1 = 0;
		}

		// The vertical distance between B and A
		int remaining = y1 - y0;
		int offset = y0 * bitmap.width;

		// The first segment
		while (remaining-- > 0) {
			// stop if we leave the boundaries
			if (++y0 > maxY) {
				return;
			}

			drawBitmapScanline(src, offset,
					x0 >> 16, u0, v0,
					x1 >> 16, u1, v1
			);

			// step toward C
			x0 += mx2;
			u0 += mu2;
			v0 += mv2;

			// step toward B
			x1 += mx0;
			u1 += mu0;
			v1 += mv0;

			// step one row down
			offset += bitmap.width;
		}

		// The vertical distance between B and C
		remaining = y2 - y1;

		// The second segment
		while (remaining-- > 0) {
			// stop if we leave the boundaries
			if (++y0 > maxY) {
				return;
			}

			drawBitmapScanline(src, offset,
					x0 >> 16, u0, v0,
					x2 >> 16, u2, v2
			);

			// step toward C
			x0 += mx2;
			u0 += mu2;
			v0 += mv2;

			// step toward C
			x2 += mx1;
			u2 += mu1;
			v2 += mv1;

			// step one row down
			offset += bitmap.width;
		}
	}

	private void drawBitmapScanline(Bitmap src, int offset, int x0, int u0, int v0, int x1, int u1, int v1) {
		// nothing to draw
		if (x0 == x1) {
			return;
		}

		// flip
		if (x0 > x1) {
			int tmp = x0;
			x0 = x1;
			x1 = tmp;

			tmp = u0;
			u0 = u1;
			u1 = tmp;

			tmp = v0;
			v0 = v1;
			v1 = tmp;
		}

		int length = x1 - x0;

		// slopes
		int mu = (u1 - u0) / length;
		int mv = (v1 - v0) / length;

		// keep within bounds
		if (x0 < minX) {
			int step = minX - x0;
			u0 += step * mu;
			v0 += step * mv;
			x0 = minX;
		}

		if (x1 > maxX) {
			x1 = maxX;
		}

		length = x1 - x0;
		offset += x0;

		if (alpha == 256) {
			while (length-- > 0) {
				int x = (u0 >> 16);
				int y = (v0 >> 16);

				// clamp
				if (x < 0) x = 0;
				if (y < 0) y = 0;
				if (x >= src.width) x = src.width - 1;
				if (y >= src.height) y = src.height - 1;

				bitmap.data[offset++] = src.data[x + src.offsetY[y]];

				u0 += mu;
				v0 += mv;
			}
		} else {
			int beta = 256 - alpha;
			while (length-- > 0) {
				int x = u0 >> 16;
				int y = v0 >> 16;

				// clamp
				if (x < 0) x = 0;
				if (y < 0) y = 0;
				if (x >= src.width) x = src.width - 1;
				if (y >= src.height) y = src.height - 1;

				bitmap.data[offset] = mix(bitmap.data[offset++], src.data[x + src.offsetY[y]], alpha, beta);

				u0 += mu;
				v0 += mv;
			}
		}
	}