From 03473abea26db2738ea42828360eab90c965697f Mon Sep 17 00:00:00 2001 From: Erick Vasquez Garcia Date: Sun, 21 Mar 2021 21:57:49 +0100 Subject: [PATCH] A software implementation for gamma, brightness, contrast and saturation effects on SoftPoly. Based on original code by @dpjudas, thanks for the code. --- .../polyrenderer/backend/poly_framebuffer.cpp | 82 ++++++++++++- .../polyrenderer/backend/poly_framebuffer.h | 116 ++++++++++++++++++ 2 files changed, 194 insertions(+), 4 deletions(-) diff --git a/src/common/rendering/polyrenderer/backend/poly_framebuffer.cpp b/src/common/rendering/polyrenderer/backend/poly_framebuffer.cpp index 7ce16369b44..a71523a4681 100644 --- a/src/common/rendering/polyrenderer/backend/poly_framebuffer.cpp +++ b/src/common/rendering/polyrenderer/backend/poly_framebuffer.cpp @@ -152,6 +152,10 @@ void PolyFrameBuffer::FlushDrawCommands() } } +EXTERN_CVAR(Float, vid_brightness) +EXTERN_CVAR(Float, vid_contrast) +EXTERN_CVAR(Float, vid_saturation) + void PolyFrameBuffer::Update() { twoD.Reset(); @@ -177,8 +181,9 @@ void PolyFrameBuffer::Update() if (dst) { #if 1 + // [GEC] with the help of dpJudas a new system of copying and applying gamma in the video buffer auto copyqueue = std::make_shared(&mFrameMemory); - copyqueue->Push(dst, pitch / pixelsize, src, w, h, w, pixelsize); + copyqueue->Push(dst, pitch / pixelsize, src, w, h, w, vid_gamma, vid_contrast, vid_brightness, vid_saturation); DrawerThreads::Execute(copyqueue); #else for (int y = 0; y < h; y++) @@ -366,6 +371,40 @@ FTexture *PolyFrameBuffer::WipeEndScreen() TArray PolyFrameBuffer::GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma) { + // [GEC] Really necessary to apply gamma, brightness, contrast and saturation for screenshot + + std::vector gammatablebuf(256); + uint8_t* gammatable = gammatablebuf.data(); + + float InvGamma = 1.0f / clamp(vid_gamma, 0.1f, 4.f); + float Brightness = clamp(vid_brightness, -0.8f, 0.8f); + float Contrast = clamp(vid_contrast, 0.1f, 3.f); + float Saturation = clamp(vid_saturation, -15.0f, 15.f); + + for (int x = 0; x < 256; x++) + { + float ramp = (float)(x / 255.f); + // Apply Contrast + // vec4 finalColor = vec4((((originalColor.rgb - vec3(0.5)) * Contrast) + vec3(0.5)), 1.0); + if(vid_contrast != 1.0f) + ramp = (((ramp - 0.5f) * Contrast) + 0.5f); + + // Apply Brightness + // vec4 finalColor = vec4(originalColor.rgb + Brightness, 1.0); + if (vid_brightness != 0.0f) + ramp += (Brightness / 2.0f); + + // Apply Gamma + // FragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma)); + if (vid_gamma != 1.0f) + ramp = pow(ramp, InvGamma); + + // Clamp ramp + ramp = clamp(ramp, 0.0f, 1.f); + + gammatable[x] = (uint8_t)(ramp * 255); + } + int w = SCREENWIDTH; int h = SCREENHEIGHT; @@ -380,9 +419,44 @@ TArray PolyFrameBuffer::GetScreenshotBuffer(int &pitch, ESSType &color_ for (int x = 0; x < w; x++) { - ScreenshotBuffer[dindex ] = pixels[sindex + 2]; - ScreenshotBuffer[dindex + 1] = pixels[sindex + 1]; - ScreenshotBuffer[dindex + 2] = pixels[sindex ]; + uint32_t red = pixels[sindex + 2]; + uint32_t green = pixels[sindex + 1]; + uint32_t blue = pixels[sindex]; + + if (vid_saturation != 1.0f) + { + float NewR = (float)(red / 255.f); + float NewG = (float)(green / 255.f); + float NewB = (float)(blue / 255.f); + + // Apply Saturation + // float luma = dot(In, float3(0.2126729, 0.7151522, 0.0721750)); + // Out = luma.xxx + Saturation.xxx * (In - luma.xxx); + //float luma = (NewR * 0.2126729f) + (NewG * 0.7151522f) + (NewB * 0.0721750f); // Rec. 709 + float luma = (NewR * 0.299f) + (NewG * 0.587f) + (NewB * 0.114f); //Rec. 601 + NewR = luma + (Saturation * (NewR - luma)); + NewG = luma + (Saturation * (NewG - luma)); + NewB = luma + (Saturation * (NewB - luma)); + + // Clamp All + NewR = clamp(NewR, 0.0f, 1.f); + NewG = clamp(NewG, 0.0f, 1.f); + NewB = clamp(NewB, 0.0f, 1.f); + + red = (uint32_t)(NewR * 255.f); + green = (uint32_t)(NewG * 255.f); + blue = (uint32_t)(NewB * 255.f); + } + + // Apply Contrast / Brightness / Gamma + red = gammatable[red]; + green = gammatable[green]; + blue = gammatable[blue]; + + ScreenshotBuffer[dindex ] = red; + ScreenshotBuffer[dindex + 1] = green; + ScreenshotBuffer[dindex + 2] = blue; + dindex += 3; sindex += 4; } diff --git a/src/common/rendering/polyrenderer/backend/poly_framebuffer.h b/src/common/rendering/polyrenderer/backend/poly_framebuffer.h index a3c4eee292d..76eae0cedd2 100644 --- a/src/common/rendering/polyrenderer/backend/poly_framebuffer.h +++ b/src/common/rendering/polyrenderer/backend/poly_framebuffer.h @@ -96,3 +96,119 @@ class PolyFrameBuffer : public SystemBaseFrameBuffer }; inline PolyFrameBuffer *GetPolyFrameBuffer() { return static_cast(screen); } + +// [GEC] Original code of dpJudas, I add the formulas of gamma, brightness, contrast and saturation. +class CopyAndApplyGammaCommand : public DrawerCommand +{ +public: + CopyAndApplyGammaCommand(void* dest, int destpitch, const void* src, int width, int height, int srcpitch, + float gamma, float contrast, float brightness, float saturation) : dest(dest), src(src), destpitch(destpitch), width(width), height(height), srcpitch(srcpitch), + gamma(gamma), contrast(contrast), brightness(brightness), saturation(saturation) + { + } + + void Execute(DrawerThread* thread) + { + float Saturation = clamp(saturation, -15.0f, 15.f); + + std::vector gammatablebuf(256); + uint8_t* gammatable = gammatablebuf.data(); + InitGammaTable(gammatable); + + int w = width; + int start = thread->skipped_by_thread(0); + int count = thread->count_for_thread(0, height); + int sstep = thread->num_cores * srcpitch; + int dstep = thread->num_cores * destpitch; + uint32_t* d = (uint32_t*)dest + start * destpitch; + const uint32_t* s = (const uint32_t*)src + start * srcpitch; + for (int y = 0; y < count; y++) + { + for (int x = 0; x < w; x++) + { + uint32_t red = RPART(s[x]); + uint32_t green = GPART(s[x]); + uint32_t blue = BPART(s[x]); + uint32_t alpha = APART(s[x]); + + if (saturation != 1.0f) + { + float NewR = (float)(red / 255.f); + float NewG = (float)(green / 255.f); + float NewB = (float)(blue / 255.f); + + // Apply Saturation + // float luma = dot(In, float3(0.2126729, 0.7151522, 0.0721750)); + // Out = luma.xxx + Saturation.xxx * (In - luma.xxx); + //float luma = (NewR * 0.2126729f) + (NewG * 0.7151522f) + (NewB * 0.0721750f); // Rec. 709 + float luma = (NewR * 0.299f) + (NewG * 0.587f) + (NewB * 0.114f); //Rec. 601 + NewR = luma + (Saturation * (NewR - luma)); + NewG = luma + (Saturation * (NewG - luma)); + NewB = luma + (Saturation * (NewB - luma)); + + // Clamp All + NewR = clamp(NewR, 0.0f, 1.f); + NewG = clamp(NewG, 0.0f, 1.f); + NewB = clamp(NewB, 0.0f, 1.f); + + red = (uint32_t)(NewR * 255.f); + green = (uint32_t)(NewG * 255.f); + blue = (uint32_t)(NewB * 255.f); + } + + // Apply Contrast / Brightness / Gamma + red = gammatable[red]; + green = gammatable[green]; + blue = gammatable[blue]; + + d[x] = MAKEARGB(alpha, (uint8_t)red, (uint8_t)green, (uint8_t)blue); + } + d += dstep; + s += sstep; + } + } + +private: + void InitGammaTable(uint8_t *gammatable) + { + float InvGamma = 1.0f / clamp(gamma, 0.1f, 4.f); + float Brightness = clamp(brightness, -0.8f, 0.8f); + float Contrast = clamp(contrast, 0.1f, 3.f); + + for (int x = 0; x < 256; x++) + { + float ramp = (float)(x / 255.f); + + // Apply Contrast + // vec4 finalColor = vec4((((originalColor.rgb - vec3(0.5)) * Contrast) + vec3(0.5)), 1.0); + if (contrast != 1.0f) + ramp = (((ramp - 0.5f) * Contrast) + 0.5f); + + // Apply Brightness + // vec4 finalColor = vec4(originalColor.rgb + Brightness, 1.0); + if (brightness != 0.0f) + ramp += (Brightness / 2.0f); + + // Apply Gamma + // FragColor.rgb = pow(fragColor.rgb, vec3(1.0/gamma)); + if (gamma != 1.0f) + ramp = pow(ramp, InvGamma); + + // Clamp ramp + ramp = clamp(ramp, 0.0f, 1.f); + + gammatable[x] = (uint8_t)(ramp * 255); + } + } + + void* dest; + const void* src; + int destpitch; + int width; + int height; + int srcpitch; + float gamma; + float contrast; + float brightness; + float saturation; +};