Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do color blending after gamma correction and increase the resolution of color calculations to 16 bit #393

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
17 changes: 11 additions & 6 deletions Software/grab/GrabberBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ void GrabberBase::setGrabInterval(int msec)
m_timer->setInterval(msec);
}

void GrabberBase::setGammaDecodeValue(double value)
{
for (int i = 0; i < 256; i++) gammaDecodeArray[i] = 65535 * pow((double)i / 255.0, value);
}

void GrabberBase::startGrabbing()
{
DEBUG_LOW_LEVEL << Q_FUNC_INFO << this->metaObject()->className();
Expand Down Expand Up @@ -142,7 +147,7 @@ void GrabberBase::grab()

for (int i = 0; i < _context->grabWidgets->size(); ++i) {
if (!_context->grabWidgets->at(i)->isAreaEnabled()) {
_context->grabResult->append(qRgb(0,0,0));
_context->grabResult->append(qRgba64(0));
continue;
}
QRect widgetRect = _context->grabWidgets->at(i)->frameGeometry();
Expand All @@ -151,7 +156,7 @@ void GrabberBase::grab()
const GrabbedScreen *grabbedScreen = screenOfRect(widgetRect);
if (grabbedScreen == NULL) {
DEBUG_HIGH_LEVEL << Q_FUNC_INFO << " widget is out of screen " << Debug::toString(widgetRect);
_context->grabResult->append(0);
_context->grabResult->append(qRgba64(0));
continue;
}
DEBUG_HIGH_LEVEL << Q_FUNC_INFO << Debug::toString(widgetRect);
Expand All @@ -164,7 +169,7 @@ void GrabberBase::grab()

DEBUG_HIGH_LEVEL << "Widget 'grabme' is out of screen:" << Debug::toString(clippedRect);

_context->grabResult->append(qRgb(0,0,0));
_context->grabResult->append(qRgba64(0));
continue;
}

Expand Down Expand Up @@ -210,16 +215,16 @@ void GrabberBase::grab()
qWarning() << Q_FUNC_INFO << " preparedRect is not valid:" << Debug::toString(preparedRect);
// width and height can't be negative

_context->grabResult->append(qRgb(0,0,0));
_context->grabResult->append(qRgba64(0));
continue;
}

const int bytesPerPixel = 4;
Q_ASSERT(grabbedScreen->imgData);
QRgb avgColor = Grab::Calculations::calculateAvgColor(
QRgba64 avgColor = Grab::Calculations::calculateAvgColor(
grabbedScreen->imgData, grabbedScreen->imgFormat,
grabbedScreen->bytesPerRow > 0 ? grabbedScreen->bytesPerRow : grabbedScreen->screenInfo.rect.width() * bytesPerPixel,
preparedRect);
preparedRect, gammaDecodeArray);
_context->grabResult->append(avgColor);
}

Expand Down
51 changes: 51 additions & 0 deletions Software/grab/WinUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,12 @@ VOID FreeRestrictedSD(PVOID ptr) {
{
PrismatikMath::applyColorTemperature(colors, _client->getSmoothenedColorTemperature(), gamma);
}


void NightLight::apply(QList<QRgba64>& colors, const double gamma)
{
PrismatikMath::applyColorTemperature(colors, _client->getSmoothenedColorTemperature(), gamma);
}
#endif // NIGHTLIGHT_SUPPORT

bool GammaRamp::isSupported()
Expand Down Expand Up @@ -425,6 +431,22 @@ VOID FreeRestrictedSD(PVOID ptr) {
return true;
}

void GammaRamp::interpolateGamma()
{
// interpolate 8 bit input to 16 bit input
for (int channel = 0; channel < 3; channel++) {
int pos = 0;
for (int value = 0; value < 255; value++) {
int delta = _gammaArray[channel][value + 1] - _gammaArray[channel][value];
for (int i = 0; i < 257; i++) {
_gammaArray16[channel][pos] = _gammaArray[channel][value] + (delta * i) / 257;
pos++;
}
}
_gammaArray16[channel][pos] = _gammaArray[channel][255];
}
}

void GammaRamp::apply(QList<QRgb>& colors, const double/*gamma*/)
{
HDC dc = NULL;
Expand Down Expand Up @@ -453,4 +475,33 @@ VOID FreeRestrictedSD(PVOID ptr) {
DeleteObject(dc);
}

void GammaRamp::apply(QList<QRgba64>& colors, const double/*gamma*/)
{
HDC dc = NULL;

// Reload the gamma ramp every 15 seconds only.
// There seems to be a memory leak somewhere in the API and we don't
// expect the ramp to change every frame.
if (time(nullptr) - _gammaAge > 15) {
if (!loadGamma(&_gammaArray, &dc))
return;
_gammaAge = time(nullptr);
interpolateGamma();
}

for (QRgba64& color : colors) {
int red = color.red();
int green = color.green();
int blue = color.blue();

red = _gammaArray16[0][red];
green = _gammaArray16[1][green];
blue = _gammaArray16[2][blue];

color = qRgba64(red, green, blue, 0);
}

DeleteObject(dc);
}

} // namespace WinUtils
66 changes: 66 additions & 0 deletions Software/grab/calculations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,44 @@ namespace {
return color;
};

template<uint8_t offsetR, uint8_t offsetG, uint8_t offsetB>
static ColorValue accumulateBufferGamma(
const int* const buff,
const size_t pitch,
const QRect& rect,
const quint16* gamma) {
const unsigned char* const buffer = (const unsigned char* const)buff;

int r = 0;
int g = 0;
int b = 0;

const int delta = rect.width() % pixelsPerStep;
if (rect.width() >= pixelsPerStep)
for (int currentY = 0; currentY < rect.height(); currentY++) {
for (int currentX = 0; currentX < rect.width() - delta; currentX += pixelsPerStep) {
const size_t index = pitch * bytesPerPixel * (rect.y() + currentY) + (rect.x() + currentX) * bytesPerPixel;
r += gamma[PIXEL_R(0)] + gamma[PIXEL_R(1)] + gamma[PIXEL_R(2)] + gamma[PIXEL_R(3)];
g += gamma[PIXEL_G(0)] + gamma[PIXEL_G(1)] + gamma[PIXEL_G(2)] + gamma[PIXEL_G(3)];
b += gamma[PIXEL_B(0)] + gamma[PIXEL_B(1)] + gamma[PIXEL_B(2)] + gamma[PIXEL_B(3)];
}
}
for (int currentX = rect.width() - delta; currentX < rect.width(); ++currentX) {
for (int currentY = 0; currentY < rect.height(); ++currentY) {
const size_t index = pitch * bytesPerPixel * (rect.y() + currentY) + (rect.x() + currentX) * bytesPerPixel;
r += gamma[PIXEL_R(0)];
g += gamma[PIXEL_G(0)];
b += gamma[PIXEL_B(0)];
}
}

const size_t count = rect.height() * rect.width();
ColorValue color;
color.r = (r / count) & 0xffff;
color.g = (g / count) & 0xffff;
color.b = (b / count) & 0xffff;
return color;
};

enum SIMDLevel {
None = 0,
Expand Down Expand Up @@ -372,5 +410,33 @@ namespace Grab {

return qRgb(color.r, color.g, color.b);
}

QRgba64 calculateAvgColor(const unsigned char* const buffer, BufferFormat bufferFormat, const size_t pitch, const QRect& rect, const quint16* gamma) {

ColorValue color;
switch (bufferFormat) {
case BufferFormatArgb:
color = accumulateBufferGamma<PIXEL_FORMAT_ARGB>((int*)buffer, pitch / bytesPerPixel, rect, gamma);
break;

case BufferFormatAbgr:
color = accumulateBufferGamma<PIXEL_FORMAT_ABGR>((int*)buffer, pitch / bytesPerPixel, rect, gamma);
break;

case BufferFormatRgba:
color = accumulateBufferGamma<PIXEL_FORMAT_RGBA>((int*)buffer, pitch / bytesPerPixel, rect, gamma);
break;

case BufferFormatBgra:
color = accumulateBufferGamma<PIXEL_FORMAT_BGRA>((int*)buffer, pitch / bytesPerPixel, rect, gamma);
break;

default:
return qRgba64(0);
break;
}

return qRgba64(color.r, color.g, color.b, 0);
}
}
}
2 changes: 2 additions & 0 deletions Software/grab/include/BlueLightReduction.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include <QList>
#include <QRgb>
#include <QRgba64>
#include <cassert>

namespace BlueLightReduction
Expand All @@ -10,6 +11,7 @@ namespace BlueLightReduction
public:
static bool isSupported() { assert(("BlueLightReduction::isSupported() is not implemented", false)); return false; }
virtual void apply(QList<QRgb>& colors, const double gamma = 1.2) = 0;
virtual void apply(QList<QRgba64>& colors, const double gamma = 1.2) = 0;
virtual ~Client() = default;
};

Expand Down
4 changes: 4 additions & 0 deletions Software/grab/include/GrabberBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include <QSharedPointer>
#include <QColor>
#include <QRgba64>
#include <QTimer>
#include "src/GrabWidget.hpp"
#include "calculations.hpp"
Expand Down Expand Up @@ -90,6 +91,7 @@ class GrabberBase : public QObject
virtual ~GrabberBase() {}

virtual const char * name() const = 0;
virtual void setGammaDecodeValue(double value);

public slots:
virtual void startGrabbing();
Expand Down Expand Up @@ -124,6 +126,7 @@ protected slots:

virtual bool isReallocationNeeded(const QList< ScreenInfo > &grabScreens) const;


protected:
const GrabbedScreen * screenOfRect(const QRect &rect) const;

Expand All @@ -141,4 +144,5 @@ protected slots:
int grabScreensCount;
QList<GrabbedScreen> _screensWithWidgets;
QScopedPointer<QTimer> m_timer;
quint16 gammaDecodeArray[256];
};
3 changes: 2 additions & 1 deletion Software/grab/include/GrabberContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include <QList>
#include <QRgb>
#include <QRgba64>

class GrabWidget;

Expand Down Expand Up @@ -96,7 +97,7 @@ class GrabberContext {
}
public:
QList<GrabWidget *> *grabWidgets;
QList<QRgb> *grabResult;
QList<QRgba64> *grabResult;


private:
Expand Down
5 changes: 5 additions & 0 deletions Software/grab/include/WinUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <windows.h>
#include <QList>
#include <QRgb>
#include <QRgba64>
#include "BlueLightReduction.hpp"

#if defined(NIGHTLIGHT_SUPPORT)
Expand Down Expand Up @@ -76,6 +77,7 @@ VOID FreeRestrictedSD(PVOID ptr);
NightLight();
~NightLight();
void apply(QList<QRgb>& colors, const double gamma);
void apply(QList<QRgba64>& colors, const double gamma);
static bool isSupported();
private:
NightLightLibrary::NightLightWrapper* _client;
Expand All @@ -87,11 +89,14 @@ VOID FreeRestrictedSD(PVOID ptr);
GammaRamp() = default;
~GammaRamp() = default;
void apply(QList<QRgb>& colors, const double/*gamma*/);
void apply(QList<QRgba64>& colors, const double/*gamma*/);
static bool isSupported();
private:
time_t _gammaAge = 0;
WORD _gammaArray[3][256];
WORD _gammaArray16[3][65536];
static bool loadGamma(LPVOID gamma, HDC* dc);
void interpolateGamma();
};
}

Expand Down
2 changes: 2 additions & 0 deletions Software/grab/include/calculations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@

#include <QRect>
#include <QRgb>
#include <QRgba64>
#include <QList>
#include "common/BufferFormat.h"

namespace Grab {
namespace Calculations {
QRgb calculateAvgColor(const unsigned char * const buffer, BufferFormat bufferFormat, const size_t pitch, const QRect &rect);
QRgba64 calculateAvgColor(const unsigned char* const buffer, BufferFormat bufferFormat, const size_t pitch, const QRect& rect, const quint16* gamma);
}
}
37 changes: 24 additions & 13 deletions Software/math/PrismatikMath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ namespace PrismatikMath

void gammaCorrection(double gamma, StructRgb & eRgb)
{
eRgb.r = 4095 * pow(eRgb.r / 4095.0, gamma);
eRgb.g = 4095 * pow(eRgb.g / 4095.0, gamma);
eRgb.b = 4095 * pow(eRgb.b / 4095.0, gamma);
eRgb.r = 65535 * pow(eRgb.r / 65535.0, gamma);
eRgb.g = 65535 * pow(eRgb.g / 65535.0, gamma);
eRgb.b = 65535 * pow(eRgb.b / 65535.0, gamma);
}

void brightnessCorrection(unsigned int brightness, StructRgb & eRgb) {
Expand All @@ -74,13 +74,24 @@ namespace PrismatikMath
gamma = 1.0 / gamma; // encoding
for (QRgb& color : colors)
{
quint8 r = ::pow((qRed(color) * wp.r) / (double)USHRT_MAX, gamma) * UCHAR_MAX;
quint8 g = ::pow((qGreen(color) * wp.g) / (double)USHRT_MAX, gamma) * UCHAR_MAX;
quint8 b = ::pow((qBlue(color) * wp.b) / (double)USHRT_MAX, gamma) * UCHAR_MAX;
quint8 r = ::pow(((double)qRed(color) * wp.r) / (double)USHRT_MAX, gamma) * UCHAR_MAX;
quint8 g = ::pow(((double)qGreen(color) * wp.g) / (double)USHRT_MAX, gamma) * UCHAR_MAX;
quint8 b = ::pow(((double)qBlue(color) * wp.b) / (double)USHRT_MAX, gamma) * UCHAR_MAX;
color = qRgb(r, g, b);
}
}

void applyColorTemperature(QList<QRgba64>& colors, const quint16 colorTemperature, double gamma) {
StructRgb wp = whitePoint(colorTemperature);
gamma = 1.0 / gamma; // encoding
for (QRgba64& color : colors)
{
quint16 r = ::pow(((double)color.red() * wp.r) / (double)(65535 * 255), gamma) * 65535;
quint16 g = ::pow(((double)color.green() * wp.g) / (double)(65535 * 255), gamma) * 65535;
quint16 b = ::pow(((double)color.blue() * wp.b) / (double)(65535 * 255), gamma) * 65535;
color = qRgba64(r, g, b, 0);
}
}

int getValueHSV(const QRgb rgb) {
return max(rgb);
Expand Down Expand Up @@ -194,10 +205,10 @@ namespace PrismatikMath
}

StructXyz toXyz(const StructRgb & rgb) {
//12bit RGB from 0 to 4095
double r = ( rgb.r / 4095.0 );
double g = ( rgb.g / 4095.0 );
double b = ( rgb.b / 4095.0 );
//16bit RGB from 0 to 65535
double r = ( rgb.r / 65535.0 );
double g = ( rgb.g / 65535.0);
double b = ( rgb.b / 65535.0);

if ( r > 0.04045 )
r = pow( (r + 0.055)/1.055, 2.4);
Expand Down Expand Up @@ -319,9 +330,9 @@ namespace PrismatikMath

StructRgb eRgb;

eRgb.r = round(r*4095);
eRgb.g = round(g*4095);
eRgb.b = round(b*4095);
eRgb.r = round(r * 65535);
eRgb.g = round(g * 65535);
eRgb.b = round(b * 65535);

return eRgb;
}
Expand Down
Loading