Allow out of bounds coordinates in glBlitFramebuffer

Device::stretchRect() now supports out of bounds coordinates.
To avoid linear interpolation errors, source coordinates now
have to be in floating point rather than integer format. Most
changes in this cl are just to accommodate that int->float
change for the source rect.

Fixes all (28) failures in:
dEQP-GLES3.functional.fbo.blit.rect

Change-Id: I8fd017e60b61f2d7d6517b0e648b324be441cddd
Reviewed-on: https://swiftshader-review.googlesource.com/14648
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/D3D9/Direct3DDevice9.cpp b/src/D3D9/Direct3DDevice9.cpp
index edeff23..f5e7e89 100644
--- a/src/D3D9/Direct3DDevice9.cpp
+++ b/src/D3D9/Direct3DDevice9.cpp
@@ -6323,7 +6323,8 @@
 		}
 		else
 		{
-			renderer->blit(source, sRect, dest, dRect, filter >= D3DTEXF_LINEAR);
+			sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, 0);
+			renderer->blit(source, sRectF, dest, dRect, filter >= D3DTEXF_LINEAR);
 		}
 	}
 
diff --git a/src/OpenGL/libGL/Device.cpp b/src/OpenGL/libGL/Device.cpp
index 736bb99..be7d10e 100644
--- a/src/OpenGL/libGL/Device.cpp
+++ b/src/OpenGL/libGL/Device.cpp
@@ -621,7 +621,8 @@
 		}
 		else
 		{
-			blit(source, sRect, dest, dRect, scaling && filter);
+			sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
+			blit(source, sRectF, dest, dRect, scaling && filter);
 		}
 
 		return true;
diff --git a/src/OpenGL/libGLES_CM/Context.cpp b/src/OpenGL/libGLES_CM/Context.cpp
index b6f76c8..398c699 100644
--- a/src/OpenGL/libGLES_CM/Context.cpp
+++ b/src/OpenGL/libGLES_CM/Context.cpp
@@ -2852,7 +2852,8 @@
 
 void Context::blit(sw::Surface *source, const sw::SliceRect &sRect, sw::Surface *dest, const sw::SliceRect &dRect)
 {
-	device->blit(source, sRect, dest, dRect, false);
+	sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
+	device->blit(source, sRectF, dest, dRect, false);
 }
 
 void Context::finish()
diff --git a/src/OpenGL/libGLES_CM/Device.cpp b/src/OpenGL/libGLES_CM/Device.cpp
index 26f53bc..23c96f4 100644
--- a/src/OpenGL/libGLES_CM/Device.cpp
+++ b/src/OpenGL/libGLES_CM/Device.cpp
@@ -506,7 +506,8 @@
 		}
 		else
 		{
-			blit(source, sRect, dest, dRect, scaling && filter);
+			sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
+			blit(source, sRectF, dest, dRect, scaling && filter);
 		}
 
 		return true;
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index 981ffe3..cdeabbd 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -3331,12 +3331,12 @@
 		return error(GL_INVALID_OPERATION);
 	}
 
-	sw::Rect rect = {x, y, x + width, y + height};
-	sw::Rect dstRect = { 0, 0, width, height };
-	rect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
+	sw::RectF rect((float)x, (float)y, (float)(x + width), (float)(y + height));
+	sw::Rect dstRect(0, 0, width, height);
+	rect.clip(0.0f, 0.0f, (float)renderTarget->getWidth(), (float)renderTarget->getHeight());
 
 	sw::Surface *externalSurface = sw::Surface::create(width, height, 1, egl::ConvertFormatType(format, type), pixels, outputPitch, outputPitch * outputHeight);
-	sw::SliceRect sliceRect(rect);
+	sw::SliceRectF sliceRect(rect);
 	sw::SliceRect dstSliceRect(dstRect);
 	device->blit(renderTarget, sliceRect, externalSurface, dstSliceRect, false);
 	delete externalSurface;
@@ -3628,7 +3628,8 @@
 
 void Context::blit(sw::Surface *source, const sw::SliceRect &sRect, sw::Surface *dest, const sw::SliceRect &dRect)
 {
-	device->blit(source, sRect, dest, dRect, false);
+	sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
+	device->blit(source, sRectF, dest, dRect, false);
 }
 
 void Context::finish()
diff --git a/src/OpenGL/libGLESv2/Device.cpp b/src/OpenGL/libGLESv2/Device.cpp
index a4ba15a..cb2f49f 100644
--- a/src/OpenGL/libGLESv2/Device.cpp
+++ b/src/OpenGL/libGLESv2/Device.cpp
@@ -537,12 +537,16 @@
 			flipY = (destRect->y0 > destRect->y1);
 		}
 
-		SliceRect sRect;
+		SliceRectF sRect;
 		SliceRect dRect;
 
 		if(sourceRect)
 		{
-			sRect = *sourceRect;
+			sRect.x0 = (float)(sourceRect->x0);
+			sRect.x1 = (float)(sourceRect->x1);
+			sRect.y0 = (float)(sourceRect->y0);
+			sRect.y1 = (float)(sourceRect->y1);
+			sRect.slice = sourceRect->slice;
 
 			if(sRect.x0 > sRect.x1)
 			{
@@ -556,10 +560,10 @@
 		}
 		else
 		{
-			sRect.y0 = 0;
-			sRect.x0 = 0;
-			sRect.y1 = sHeight;
-			sRect.x1 = sWidth;
+			sRect.y0 = 0.0f;
+			sRect.x0 = 0.0f;
+			sRect.y1 = (float)sHeight;
+			sRect.x1 = (float)sWidth;
 		}
 
 		if(destRect)
@@ -584,17 +588,131 @@
 			dRect.x1 = dWidth;
 		}
 
+		if(sRect.x0 < 0)
+		{
+			float ratio = static_cast<float>(dRect.width()) / sRect.width();
+			float offsetf = roundf(-sRect.x0 * ratio);
+			int offset = static_cast<int>(offsetf);
+			if(flipX)
+			{
+				dRect.x1 -= offset;
+			}
+			else
+			{
+				dRect.x0 += offset;
+			}
+			sRect.x0 += offsetf / ratio;
+		}
+		if(sRect.x1 > sWidth)
+		{
+			float ratio = static_cast<float>(dRect.width()) / sRect.width();
+			float offsetf = roundf((sRect.x1 - (float)sWidth) * ratio);
+			int offset = static_cast<int>(offsetf);
+			if(flipX)
+			{
+				dRect.x0 += offset;
+			}
+			else
+			{
+				dRect.x1 -= offset;
+			}
+			sRect.x1 -= offsetf / ratio;
+		}
+		if(sRect.y0 < 0)
+		{
+			float ratio = static_cast<float>(dRect.height()) / sRect.height();
+			float offsetf = roundf(-sRect.y0 * ratio);
+			int offset = static_cast<int>(offsetf);
+			if(flipY)
+			{
+				dRect.y1 -= offset;
+			}
+			else
+			{
+				dRect.y0 += offset;
+			}
+			sRect.y0 += offsetf / ratio;
+		}
+		if(sRect.y1 > sHeight)
+		{
+			float ratio = static_cast<float>(dRect.height()) / sRect.height();
+			float offsetf = roundf((sRect.y1 - (float)sHeight) * ratio);
+			int offset = static_cast<int>(offsetf);
+			if(flipY)
+			{
+				dRect.y0 += offset;
+			}
+			else
+			{
+				dRect.y1 -= offset;
+			}
+			sRect.y1 -= offsetf / ratio;
+		}
+
+		if(dRect.x0 < 0)
+		{
+			float offset = (static_cast<float>(-dRect.x0) / static_cast<float>(dRect.width())) * sRect.width();
+			if(flipX)
+			{
+				sRect.x1 -= offset;
+			}
+			else
+			{
+				sRect.x0 += offset;
+			}
+			dRect.x0 = 0;
+		}
+		if(dRect.x1 > dWidth)
+		{
+			float offset = (static_cast<float>(dRect.x1 - dWidth) / static_cast<float>(dRect.width())) * sRect.width();
+			if(flipX)
+			{
+				sRect.x0 += offset;
+			}
+			else
+			{
+				sRect.x1 -= offset;
+			}
+			dRect.x1 = dWidth;
+		}
+		if(dRect.y0 < 0)
+		{
+			float offset = (static_cast<float>(-dRect.y0) / static_cast<float>(dRect.height())) * sRect.height();
+			if(flipY)
+			{
+				sRect.y1 -= offset;
+			}
+			else
+			{
+				sRect.y0 += offset;
+			}
+			dRect.y0 = 0;
+		}
+		if(dRect.y1 > dHeight)
+		{
+			float offset = (static_cast<float>(dRect.y1 - dHeight) / static_cast<float>(dRect.height())) * sRect.height();
+			if(flipY)
+			{
+				sRect.y0 += offset;
+			}
+			else
+			{
+				sRect.y1 -= offset;
+			}
+			dRect.y1 = dHeight;
+		}
+
 		if(!validRectangle(&sRect, source) || !validRectangle(&dRect, dest))
 		{
 			ERR("Invalid parameters");
 			return false;
 		}
 
-		bool scaling = (sRect.x1 - sRect.x0 != dRect.x1 - dRect.x0) || (sRect.y1 - sRect.y0 != dRect.y1 - dRect.y0);
+		bool scaling = (sRect.width() != (float)dRect.width()) || (sRect.height() != (float)dRect.height());
 		bool equalFormats = source->getInternalFormat() == dest->getInternalFormat();
 		bool hasQuadLayout = Surface::hasQuadLayout(source->getInternalFormat()) || Surface::hasQuadLayout(dest->getInternalFormat());
-		bool fullCopy = (sRect.x0 == 0) && (sRect.y0 == 0) && (dRect.x0 == 0) && (dRect.y0 == 0) &&
-		                (sRect.x1 == sWidth) && (sRect.y1 == sHeight) && (dRect.x1 == dWidth) && (dRect.y0 == dHeight);
+		bool fullCopy = (sRect.x0 == 0.0f) && (sRect.y0 == 0.0f) && (dRect.x0 == 0) && (dRect.y0 == 0) &&
+		                (sRect.x1 == (float)sWidth) && (sRect.y1 == (float)sHeight) && (dRect.x1 == dWidth) && (dRect.y0 == dHeight);
 		bool isDepth = (flags & Device::DEPTH_BUFFER) && egl::Image::isDepth(source->getInternalFormat());
 		bool isStencil = (flags & Device::STENCIL_BUFFER) && (egl::Image::isDepth(source->getInternalFormat()) || egl::Image::isStencil(source->getInternalFormat()));
 		bool isColor = (flags & Device::COLOR_BUFFER) == Device::COLOR_BUFFER;
@@ -611,7 +729,7 @@
 		{
 			if(source->hasDepth() && isDepth)
 			{
-				sw::byte *sourceBuffer = (sw::byte*)source->lockInternal(sRect.x0, sRect.y0, 0, LOCK_READONLY, PUBLIC);
+				sw::byte *sourceBuffer = (sw::byte*)source->lockInternal((int)sRect.x0, (int)sRect.y0, 0, LOCK_READONLY, PUBLIC);
 				sw::byte *destBuffer = (sw::byte*)dest->lockInternal(dRect.x0, dRect.y0, 0, LOCK_DISCARD, PUBLIC);
 
 				copyBuffer(sourceBuffer, destBuffer, dRect.width(), dRect.height(), source->getInternalPitchB(), dest->getInternalPitchB(), egl::Image::bytes(source->getInternalFormat()), flipX, flipY);
@@ -622,7 +740,7 @@
 
 			if(source->hasStencil() && isStencil)
 			{
-				sw::byte *sourceBuffer = (sw::byte*)source->lockStencil(sRect.x0, sRect.y0, 0, PUBLIC);
+				sw::byte *sourceBuffer = (sw::byte*)source->lockStencil((int)sRect.x0, (int)sRect.y0, 0, PUBLIC);
 				sw::byte *destBuffer = (sw::byte*)dest->lockStencil(dRect.x0, dRect.y0, 0, PUBLIC);
 
 				copyBuffer(sourceBuffer, destBuffer, source->getWidth(), source->getHeight(), source->getStencilPitchB(), dest->getStencilPitchB(), egl::Image::bytes(source->getStencilFormat()), flipX, flipY);
@@ -633,7 +751,7 @@
 		}
 		else if((flags & Device::COLOR_BUFFER) && !scaling && equalFormats && (!hasQuadLayout || fullCopy))
 		{
-			unsigned char *sourceBytes = (unsigned char*)source->lockInternal(sRect.x0, sRect.y0, sourceRect->slice, LOCK_READONLY, PUBLIC);
+			unsigned char *sourceBytes = (unsigned char*)source->lockInternal((int)sRect.x0, (int)sRect.y0, sourceRect->slice, LOCK_READONLY, PUBLIC);
 			unsigned char *destBytes = (unsigned char*)dest->lockInternal(dRect.x0, dRect.y0, destRect->slice, LOCK_READWRITE, PUBLIC);
 			unsigned int sourcePitch = source->getInternalPitchB();
 			unsigned int destPitch = dest->getInternalPitchB();
@@ -667,7 +785,9 @@
 			{
 				swap(dRect.y0, dRect.y1);
 			}
-			blit(source, sRect, dest, dRect, scaling && (flags & Device::USE_FILTER), isStencil);
+
+			SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
+			blit(source, sRectF, dest, dRect, scaling && (flags & Device::USE_FILTER), isStencil);
 		}
 		else
 		{
@@ -886,7 +1006,32 @@
 			return false;
 		}
 
-		if(rect->x1 > (int)surface->getWidth() || rect->y1 > (int)surface->getHeight())
+		if(rect->x1 >(int)surface->getWidth() || rect->y1 >(int)surface->getHeight())
+		{
+			return false;
+		}
+
+		return true;
+	}
+
+	bool Device::validRectangle(const sw::RectF *rect, sw::Surface *surface)
+	{
+		if(!rect)
+		{
+			return true;
+		}
+
+		if(rect->x1 <= rect->x0 || rect->y1 <= rect->y0)
+		{
+			return false;
+		}
+
+		if(rect->x0 < 0 || rect->y0 < 0)
+		{
+			return false;
+		}
+
+		if(rect->x1 >(float)surface->getWidth() || rect->y1 >(float)surface->getHeight())
 		{
 			return false;
 		}
diff --git a/src/OpenGL/libGLESv2/Device.hpp b/src/OpenGL/libGLESv2/Device.hpp
index 17c7925..1b1492e 100644
--- a/src/OpenGL/libGLESv2/Device.hpp
+++ b/src/OpenGL/libGLESv2/Device.hpp
@@ -85,6 +85,7 @@
 		bool bindViewport();   // Also adjusts for scissoring
 
 		bool validRectangle(const sw::Rect *rect, sw::Surface *surface);
+		bool validRectangle(const sw::RectF *rect, sw::Surface *surface);
 
 		void copyBuffer(sw::byte *sourceBuffer, sw::byte *destBuffer, unsigned int width, unsigned int height, unsigned int sourcePitch, unsigned int destPitch, unsigned int bytes, bool flipX, bool flipY);
 
diff --git a/src/Renderer/Blitter.cpp b/src/Renderer/Blitter.cpp
index b2486fb..810f3cd 100644
--- a/src/Renderer/Blitter.cpp
+++ b/src/Renderer/Blitter.cpp
@@ -39,8 +39,7 @@
 
 		sw::Surface *color = sw::Surface::create(1, 1, 1, format, pixel, sw::Surface::bytes(format), sw::Surface::bytes(format));
 		Blitter::Options clearOptions = static_cast<sw::Blitter::Options>((rgbaMask & 0xF) | CLEAR_OPERATION);
-		SliceRect sRect(dRect);
-		sRect.slice = 0;
+		SliceRectF sRect((float)dRect.x0, (float)dRect.y0, (float)dRect.x1, (float)dRect.y1, 0);
 		blit(color, sRect, dest, dRect, clearOptions);
 		delete color;
 	}
@@ -127,7 +126,7 @@
 		return true;
 	}
 
-	void Blitter::blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
+	void Blitter::blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
 	{
 		Blitter::Options options = WRITE_RGBA;
 		if(filter)
@@ -141,7 +140,7 @@
 		blit(source, sRect, dest, dRect, options);
 	}
 
-	void Blitter::blit(Surface *source, const SliceRect &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
+	void Blitter::blit(Surface *source, const SliceRectF &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
 	{
 		if(dest->getInternalFormat() == FORMAT_NULL)
 		{
@@ -153,7 +152,7 @@
 			return;
 		}
 
-		SliceRect sRect = sourceRect;
+		SliceRectF sRect = sourceRect;
 		SliceRect dRect = destRect;
 
 		bool flipX = destRect.x0 > destRect.x1;
@@ -170,14 +169,14 @@
 			swap(sRect.y0, sRect.y1);
 		}
 
-		source->lockInternal(sRect.x0, sRect.y0, sRect.slice, sw::LOCK_READONLY, sw::PUBLIC);
+		source->lockInternal((int)sRect.x0, (int)sRect.y0, sRect.slice, sw::LOCK_READONLY, sw::PUBLIC);
 		dest->lockInternal(dRect.x0, dRect.y0, dRect.slice, sw::LOCK_WRITEONLY, sw::PUBLIC);
 
-		float w = static_cast<float>(sRect.x1 - sRect.x0) / static_cast<float>(dRect.x1 - dRect.x0);
-		float h = static_cast<float>(sRect.y1 - sRect.y0) / static_cast<float>(dRect.y1 - dRect.y0);
+		float w = sRect.width() / dRect.width();
+		float h = sRect.height() / dRect.height();
 
-		const float xStart = (float)sRect.x0 + 0.5f * w;
-		float y = (float)sRect.y0 + 0.5f * h;
+		const float xStart = sRect.x0 + 0.5f * w;
+		float y = sRect.y0 + 0.5f * h;
 
 		for(int j = dRect.y0; j < dRect.y1; j++)
 		{
@@ -1261,8 +1260,10 @@
 							Int X0 = Max(Int(x0), 0);
 							Int Y0 = Max(Int(y0), 0);
 
-							Int X1 = IfThenElse(X0 + 1 >= sWidth, X0, X0 + 1);
-							Int Y1 = IfThenElse(Y0 + 1 >= sHeight, Y0, Y0 + 1);
+							Int X1 = X0 + 1;
+							Int Y1 = Y0 + 1;
+							X1 = IfThenElse(X1 >= sWidth, X0, X1);
+							Y1 = IfThenElse(Y1 >= sHeight, Y0, Y1);
 
 							Pointer<Byte> s00 = source + ComputeOffset(X0, Y0, sPitchB, srcBytes, srcQuadLayout);
 							Pointer<Byte> s01 = source + ComputeOffset(X1, Y0, sPitchB, srcBytes, srcQuadLayout);
@@ -1276,11 +1277,11 @@
 
 							Float4 fx = Float4(x0 - Float(X0));
 							Float4 fy = Float4(y0 - Float(Y0));
+							Float4 ix = Float4(1.0f) - fx;
+							Float4 iy = Float4(1.0f) - fy;
 
-							color = c00 * (Float4(1.0f) - fx) * (Float4(1.0f) - fy) +
-							        c01 * fx * (Float4(1.0f) - fy) +
-							        c10 * (Float4(1.0f) - fx) * fy +
-							        c11 * fx * fy;
+							color = (c00 * ix + c01 * fx) * iy +
+							        (c10 * ix + c11 * fx) * fy;
 						}
 
 						if(!ApplyScaleAndClamp(color, state) || !write(color, d, state.destFormat, state.options))
@@ -1299,12 +1300,12 @@
 		return function(L"BlitRoutine");
 	}
 
-	bool Blitter::blitReactor(Surface *source, const SliceRect &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
+	bool Blitter::blitReactor(Surface *source, const SliceRectF &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
 	{
 		ASSERT(!(options & CLEAR_OPERATION) || ((source->getWidth() == 1) && (source->getHeight() == 1) && (source->getDepth() == 1)));
 
 		Rect dRect = destRect;
-		Rect sRect = sourceRect;
+		RectF sRect = sourceRect;
 		if(destRect.x0 > destRect.x1)
 		{
 			swap(dRect.x0, dRect.x1);
@@ -1358,10 +1359,10 @@
 		data.sPitchB = isStencil ? source->getStencilPitchB() : source->getPitchB(useSourceInternal);
 		data.dPitchB = isStencil ? dest->getStencilPitchB() : dest->getPitchB(useDestInternal);
 
-		data.w = 1.0f / (dRect.x1 - dRect.x0) * (sRect.x1 - sRect.x0);
-		data.h = 1.0f / (dRect.y1 - dRect.y0) * (sRect.y1 - sRect.y0);
-		data.x0 = (float)sRect.x0 + 0.5f * data.w;
-		data.y0 = (float)sRect.y0 + 0.5f * data.h;
+		data.w = sRect.width() / dRect.width();
+		data.h = sRect.height() / dRect.height();
+		data.x0 = sRect.x0 + 0.5f * data.w;
+		data.y0 = sRect.y0 + 0.5f * data.h;
 
 		data.x0d = dRect.x0;
 		data.x1d = dRect.x1;
diff --git a/src/Renderer/Blitter.hpp b/src/Renderer/Blitter.hpp
index f04dc24..ce7c789 100644
--- a/src/Renderer/Blitter.hpp
+++ b/src/Renderer/Blitter.hpp
@@ -76,7 +76,7 @@
 		virtual ~Blitter();
 
 		void clear(void* pixel, sw::Format format, Surface *dest, const SliceRect &dRect, unsigned int rgbaMask);
-		void blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil = false);
+		void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil = false);
 		void blit3D(Surface *source, Surface *dest);
 
 	private:
@@ -89,8 +89,8 @@
 		static bool GetScale(float4& scale, Format format);
 		static bool ApplyScaleAndClamp(Float4& value, const BlitState& state);
 		static Int ComputeOffset(Int& x, Int& y, Int& pitchB, int bytes, bool quadLayout);
-		void blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
-		bool blitReactor(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
+		void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
+		bool blitReactor(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
 		Routine *generate(BlitState &state);
 
 		RoutineCache<BlitState> *blitCache;
diff --git a/src/Renderer/Renderer.cpp b/src/Renderer/Renderer.cpp
index c8fc98f..b09c414 100644
--- a/src/Renderer/Renderer.cpp
+++ b/src/Renderer/Renderer.cpp
@@ -682,7 +682,7 @@
 		}
 	}
 
-	void Renderer::blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
+	void Renderer::blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
 	{
 		blitter->blit(source, sRect, dest, dRect, filter, isStencil);
 	}
diff --git a/src/Renderer/Renderer.hpp b/src/Renderer/Renderer.hpp
index 108f84b..bf74f79 100644
--- a/src/Renderer/Renderer.hpp
+++ b/src/Renderer/Renderer.hpp
@@ -322,7 +322,7 @@
 		void draw(DrawType drawType, unsigned int indexOffset, unsigned int count, bool update = true);
 
 		void clear(void *value, Format format, Surface *dest, const Rect &rect, unsigned int rgbaMask);
-		void blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil = false);
+		void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil = false);
 		void blit3D(Surface *source, Surface *dest);
 
 		void setIndexBuffer(Resource *indexBuffer);
diff --git a/src/Renderer/Surface.cpp b/src/Renderer/Surface.cpp
index 12d990a..d836614 100644
--- a/src/Renderer/Surface.cpp
+++ b/src/Renderer/Surface.cpp
@@ -42,14 +42,6 @@
 	unsigned int *Surface::palette = 0;
 	unsigned int Surface::paletteID = 0;
 
-	void Rect::clip(int minX, int minY, int maxX, int maxY)
-	{
-		x0 = clamp(x0, minX, maxX);
-		y0 = clamp(y0, minY, maxY);
-		x1 = clamp(x1, minX, maxX);
-		y1 = clamp(y1, minY, maxY);
-	}
-
 	void Surface::Buffer::write(int x, int y, int z, const Color<float> &color)
 	{
 		void *element = (unsigned char*)buffer + (x + border) * bytes + (y + border) * pitchB + z * sliceB;
diff --git a/src/Renderer/Surface.hpp b/src/Renderer/Surface.hpp
index fbfe063..02be486 100644
--- a/src/Renderer/Surface.hpp
+++ b/src/Renderer/Surface.hpp
@@ -23,31 +23,43 @@
 {
 	class Resource;
 
-	struct Rect
+	template <typename T> struct RectT
 	{
-		Rect() {}
-		Rect(int x0i, int y0i, int x1i, int y1i) : x0(x0i), y0(y0i), x1(x1i), y1(y1i) {}
+		RectT() {}
+		RectT(T x0i, T y0i, T x1i, T y1i) : x0(x0i), y0(y0i), x1(x1i), y1(y1i) {}
 
-		void clip(int minX, int minY, int maxX, int maxY);
+		void clip(T minX, T minY, T maxX, T maxY)
+		{
+			x0 = clamp(x0, minX, maxX);
+			y0 = clamp(y0, minY, maxY);
+			x1 = clamp(x1, minX, maxX);
+			y1 = clamp(y1, minY, maxY);
+		}
 
-		int width() const  { return x1 - x0; }
-		int height() const { return y1 - y0; }
+		T width() const  { return x1 - x0; }
+		T height() const { return y1 - y0; }
 
-		int x0;   // Inclusive
-		int y0;   // Inclusive
-		int x1;   // Exclusive
-		int y1;   // Exclusive
+		T x0;   // Inclusive
+		T y0;   // Inclusive
+		T x1;   // Exclusive
+		T y1;   // Exclusive
 	};
 
-	struct SliceRect : public Rect
+	typedef RectT<int> Rect;
+	typedef RectT<float> RectF;
+
+	template <typename T> struct SliceRectT : public RectT<T>
 	{
-		SliceRect() : slice(0) {}
-		SliceRect(const Rect& rect) : Rect(rect), slice(0) {}
-		SliceRect(const Rect& rect, int s) : Rect(rect), slice(s) {}
-		SliceRect(int x0, int y0, int x1, int y1, int s) : Rect(x0, y0, x1, y1), slice(s) {}
+		SliceRectT() : slice(0) {}
+		SliceRectT(const RectT<T>& rect) : RectT<T>(rect), slice(0) {}
+		SliceRectT(const RectT<T>& rect, int s) : RectT<T>(rect), slice(s) {}
+		SliceRectT(T x0, T y0, T x1, T y1, int s) : RectT<T>(x0, y0, x1, y1), slice(s) {}
 		int slice;
 	};
 
+	typedef SliceRectT<int> SliceRect;
+	typedef SliceRectT<float> SliceRectF;
+
 	enum Format : unsigned char
 	{
 		FORMAT_NULL,