Implement sRGB conversion in the blitter.

Note that glReadPixels() does not perform sRGB conversion.

Change-Id: I3f9089b79652ce42cb5695d5b6a8ce92d15c27a8
Reviewed-on: https://swiftshader-review.googlesource.com/14492
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index 05b7eaa..5862d2d 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -3341,7 +3341,7 @@
 	sw::Surface *externalSurface = sw::Surface::create(width, height, 1, egl::ConvertFormatType(format, type), pixels, outputPitch, outputPitch * outputHeight);
 	sw::SliceRectF sliceRect(rect);
 	sw::SliceRect dstSliceRect(dstRect);
-	device->blit(renderTarget, sliceRect, externalSurface, dstSliceRect, false);
+	device->blit(renderTarget, sliceRect, externalSurface, dstSliceRect, false, false, false);
 	delete externalSurface;
 
 	renderTarget->release();
diff --git a/src/Renderer/Blitter.cpp b/src/Renderer/Blitter.cpp
index dffc10f..81cac44 100644
--- a/src/Renderer/Blitter.cpp
+++ b/src/Renderer/Blitter.cpp
@@ -14,6 +14,7 @@
 
 #include "Blitter.hpp"
 
+#include "Shader/ShaderCore.hpp"
 #include "Reactor/Reactor.hpp"
 #include "Common/Memory.hpp"
 #include "Common/Debug.hpp"
@@ -1101,7 +1102,25 @@
 			return false;
 		}
 
-		if(unscale != scale)
+		bool srcSRGB = Surface::isSRGBformat(state.sourceFormat);
+		bool dstSRGB = Surface::isSRGBformat(state.destFormat);
+
+		if(state.convertSRGB && (srcSRGB ^ dstSRGB))   // One of the formats is sRGB encoded.
+		{
+			value = value * Float4(1.0f / unscale.x, 1.0f / unscale.y, 1.0f / unscale.z, 1.0f / unscale.w);
+
+			if(srcSRGB)
+			{
+				value = sRGBtoLinear(value);
+			}
+			else   // dstSRGB
+			{
+				value = LinearToSRGB(value);
+			}
+
+			value = value * Float4(scale.x, scale.y, scale.z, scale.w);
+		}
+		else if(unscale != scale)
 		{
 			value *= Float4(scale.x / unscale.x, scale.y / unscale.y, scale.z / unscale.z, scale.w / unscale.w);
 		}
@@ -1133,6 +1152,30 @@
 		}
 	}
 
+	Float4 Blitter::LinearToSRGB(Float4 &c)
+	{
+		Float4 lc = Min(c, Float4(0.0031308f)) * Float4(12.92f);
+		Float4 ec = Float4(1.055f) * power(c, Float4(1.0f / 2.4f)) - Float4(0.055f);
+
+		Float4 s = c;
+		s.xyz = Max(lc, ec);
+
+		return s;
+	}
+
+	Float4 Blitter::sRGBtoLinear(Float4 &c)
+	{
+		Float4 lc = c * Float4(1.0f / 12.92f);
+		Float4 ec = power((c + Float4(0.055f)) * Float4(1.0f / 1.055f), Float4(2.4f));
+
+		Int4 linear = CmpLT(c, Float4(0.04045f));
+
+		Float4 s = c;
+		s.xyz = As<Float4>((linear & As<Int4>(lc)) | (~linear & As<Int4>(ec)));   // FIXME: IfThenElse()
+
+		return s;
+	}
+
 	Routine *Blitter::generate(const State &state)
 	{
 		Function<Void(Pointer<Byte>)> function;
diff --git a/src/Renderer/Blitter.hpp b/src/Renderer/Blitter.hpp
index 66ef1f8..a132a7e 100644
--- a/src/Renderer/Blitter.hpp
+++ b/src/Renderer/Blitter.hpp
@@ -28,10 +28,10 @@
 		struct Options
 		{
 			Options() {}
-			Options(bool filter, bool useStencil)
-				: writeMask(0xF), clearOperation(false), filter(filter), useStencil(useStencil) {}
+			Options(bool filter, bool useStencil, bool convertSRGB)
+				: writeMask(0xF), clearOperation(false), filter(filter), useStencil(useStencil), convertSRGB(convertSRGB) {}
 			Options(unsigned int writeMask)
-				: writeMask(writeMask), clearOperation(true), filter(false), useStencil(false) {}
+				: writeMask(writeMask), clearOperation(true), filter(false), useStencil(false), convertSRGB(true) {}
 
 			union
 			{
@@ -49,6 +49,7 @@
 			bool clearOperation : 1;
 			bool filter : 1;
 			bool useStencil : 1;
+			bool convertSRGB : 1;
 		};
 
 		struct State : Options
@@ -106,6 +107,8 @@
 		static bool GetScale(float4& scale, Format format);
 		static bool ApplyScaleAndClamp(Float4 &value, const State &state);
 		static Int ComputeOffset(Int &x, Int &y, Int &pitchB, int bytes, bool quadLayout);
+		static Float4 LinearToSRGB(Float4 &color);
+		static Float4 sRGBtoLinear(Float4 &color);
 		bool blitReactor(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Options &options);
 		Routine *generate(const State &state);
 
diff --git a/src/Renderer/Renderer.cpp b/src/Renderer/Renderer.cpp
index ffdb547..dbde7e3 100644
--- a/src/Renderer/Renderer.cpp
+++ b/src/Renderer/Renderer.cpp
@@ -682,9 +682,9 @@
 		blitter->clear(value, format, dest, clearRect, rgbaMask);
 	}
 
-	void Renderer::blit(Surface *source, const SliceRectF &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, bool sRGBconversion)
 	{
-		blitter->blit(source, sRect, dest, dRect, {filter, isStencil});
+		blitter->blit(source, sRect, dest, dRect, {filter, isStencil, sRGBconversion});
 	}
 
 	void Renderer::blit3D(Surface *source, Surface *dest)
diff --git a/src/Renderer/Renderer.hpp b/src/Renderer/Renderer.hpp
index bf74f79..8893fb5 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 SliceRectF &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, bool sRGBconversion = true);
 		void blit3D(Surface *source, Surface *dest);
 
 		void setIndexBuffer(Resource *indexBuffer);