[kandinsky] Basics of post-processing

This commit is contained in:
Jean-Baptiste Boric
2019-03-30 17:05:04 +00:00
parent b5520fcc03
commit 9a5bfb944d
15 changed files with 368 additions and 12 deletions

View File

@@ -6,7 +6,7 @@
class Window : public View {
public:
Window() : m_contentView(nullptr) {}
void redraw(bool force = false);
virtual void redraw(bool force = false);
void setContentView(View * contentView);
protected:
#if ESCHER_VIEW_LOGGING

View File

@@ -12,6 +12,10 @@ kandinsky_src += $(addprefix kandinsky/src/,\
framebuffer_context.cpp \
ion_context.cpp \
point.cpp \
postprocess_context.cpp \
postprocess_gamma_context.cpp \
postprocess_invert_context.cpp \
postprocess_zoom_context.cpp \
rect.cpp \
)

View File

@@ -9,6 +9,10 @@
#include <kandinsky/framebuffer_context.h>
#include <kandinsky/ion_context.h>
#include <kandinsky/point.h>
#include <kandinsky/postprocess_context.h>
#include <kandinsky/postprocess_gamma_context.h>
#include <kandinsky/postprocess_invert_context.h>
#include <kandinsky/postprocess_zoom_context.h>
#include <kandinsky/rect.h>
#include <kandinsky/size.h>

View File

@@ -32,6 +32,7 @@ public:
}
static KDColor blend(KDColor first, KDColor second, uint8_t alpha);
KDColor invert() const { return KDColor(~m_value); }
operator uint16_t() const { return m_value; }
private:
constexpr KDColor(uint16_t value) : m_value(value) {}

View File

@@ -5,10 +5,15 @@
#include <kandinsky/rect.h>
#include <kandinsky/font.h>
class KDPostProcessContext;
class KDContext {
friend KDPostProcessContext;
public:
void setOrigin(KDPoint origin);
void setClippingRect(KDRect clippingRect);
KDPoint origin() const { return m_origin; }
KDRect clippingRect() const { return m_clippingRect; }
virtual void setOrigin(KDPoint origin);
virtual void setClippingRect(KDRect clippingRect);
// Pixel manipulation
void setPixel(KDPoint p, KDColor c);
@@ -28,11 +33,12 @@ public:
void fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer);
void blendRectWithMask(KDRect rect, KDColor color, const uint8_t * mask, KDColor * workingBuffer);
void strokeRect(KDRect rect, KDColor color);
protected:
KDContext(KDPoint origin, KDRect clippingRect);
virtual void pushRect(KDRect, const KDColor * pixels) = 0;
virtual void pushRectUniform(KDRect rect, KDColor color) = 0;
virtual void pullRect(KDRect rect, KDColor * pixels) = 0;
protected:
KDContext(KDPoint origin, KDRect clippingRect);
private:
KDRect absoluteFillRect(KDRect rect);
KDPoint pushOrPullString(const char * text, KDPoint p, const KDFont * font, KDColor textColor, KDColor backgroundColor, int maxByteLength, bool push, int * result = nullptr);

View File

@@ -2,15 +2,38 @@
#define KANDINSKY_ION_CONTEXT_H
#include <kandinsky/context.h>
#include <kandinsky/postprocess_gamma_context.h>
#include <kandinsky/postprocess_invert_context.h>
#include <kandinsky/postprocess_zoom_context.h>
class KDIonContext : public KDContext {
class KDRealIonContext : public KDContext {
public:
static KDIonContext * sharedContext();
private:
KDIonContext();
KDRealIonContext();
void pushRect(KDRect rect, const KDColor * pixels) override;
void pushRectUniform(KDRect rect, KDColor color) override;
void pullRect(KDRect rect, KDColor * pixels) override;
};
class KDIonContext : public KDContext {
public:
static KDIonContext * sharedContext();
void updatePostProcessingEffects();
KDPostProcessInvertContext invert;
KDPostProcessZoomContext zoom;
KDPostProcessGammaContext gamma;
bool invertEnabled;
bool zoomEnabled;
bool zoomInhibit;
bool gammaEnabled;
int zoomPosition;
private:
KDIonContext();
void pushRect(KDRect rect, const KDColor * pixels) override;
void pushRectUniform(KDRect rect, KDColor color) override;
void pullRect(KDRect rect, KDColor * pixels) override;
KDContext *rootContext;
KDRealIonContext m_realContext;
};
#endif

View File

@@ -0,0 +1,20 @@
#ifndef KANDINSKY_POSTPROCESS_CONTEXT_H
#define KANDINSKY_POSTPROCESS_CONTEXT_H
#include <kandinsky/context.h>
class KDPostProcessContext : public KDContext {
public:
virtual void setOrigin(KDPoint origin) override;
virtual void setClippingRect(KDRect clippingRect) override;
void setTarget(KDContext * context);
protected:
KDPostProcessContext();
virtual void pushRect(KDRect rect, const KDColor * pixels) override;
virtual void pushRectUniform(KDRect rect, KDColor color) override;
virtual void pullRect(KDRect rect, KDColor * pixels) override;
private:
KDContext * m_target;
};
#endif

View File

@@ -0,0 +1,19 @@
#ifndef KANDINSKY_POSTPROCESS_GAMMA_CONTEXT_H
#define KANDINSKY_POSTPROCESS_GAMMA_CONTEXT_H
#include <kandinsky/postprocess_context.h>
class KDPostProcessGammaContext : public KDPostProcessContext {
public:
KDPostProcessGammaContext();
void gamma(float& red, float& green, float& blue);
void gamma(int& red, int& green, int& blue);
void setGamma(int red, int green, int blue);
private:
void pushRect(KDRect rect, const KDColor * pixels) override;
void pushRectUniform(KDRect rect, KDColor color) override;
void pullRect(KDRect rect, KDColor * pixels) override;
int m_redGamma, m_greenGamma, m_blueGamma;
};
#endif

View File

@@ -0,0 +1,15 @@
#ifndef KANDINSKY_POSTPROCESS_INVERT_CONTEXT_H
#define KANDINSKY_POSTPROCESS_INVERT_CONTEXT_H
#include <kandinsky/postprocess_context.h>
class KDPostProcessInvertContext : public KDPostProcessContext {
public:
KDPostProcessInvertContext() = default;
private:
void pushRect(KDRect rect, const KDColor * pixels) override;
void pushRectUniform(KDRect rect, KDColor color) override;
void pullRect(KDRect rect, KDColor * pixels) override;
};
#endif

View File

@@ -0,0 +1,20 @@
#ifndef KANDINSKY_POSTPROCESS_ZOOM_CONTEXT_H
#define KANDINSKY_POSTPROCESS_ZOOM_CONTEXT_H
#include <kandinsky/postprocess_context.h>
class KDPostProcessZoomContext : public KDPostProcessContext {
public:
KDPostProcessZoomContext();
KDRect viewingArea() const { return m_viewingArea; }
void setViewingArea(KDRect viewingArea) { m_viewingArea = viewingArea; }
KDRect targetArea() const { return m_targetArea; }
void setTargetArea(KDRect targetArea) { m_targetArea = targetArea; }
private:
void pushRect(KDRect rect, const KDColor * pixels) override;
void pushRectUniform(KDRect rect, KDColor color) override;
void pullRect(KDRect rect, KDColor * pixels) override;
KDRect m_viewingArea, m_targetArea;
};
#endif

View File

@@ -1,11 +1,40 @@
#include <kandinsky/ion_context.h>
#include <ion.h>
KDRealIonContext::KDRealIonContext() : KDContext(KDPointZero, KDRect(0, 0, Ion::Display::Width, Ion::Display::Height)) {}
void KDRealIonContext::pushRect(KDRect rect, const KDColor * pixels) {
Ion::Display::pushRect(rect, pixels);
}
void KDRealIonContext::pushRectUniform(KDRect rect, KDColor color) {
Ion::Display::pushRectUniform(rect, color);
}
void KDRealIonContext::pullRect(KDRect rect, KDColor * pixels) {
Ion::Display::pullRect(rect, pixels);
}
KDIonContext * KDIonContext::sharedContext() {
static KDIonContext context;
return &context;
}
void KDIonContext::updatePostProcessingEffects() {
rootContext = &m_realContext;
if (invertEnabled) {
invert.setTarget(rootContext);
rootContext = &invert;
}
if (zoomEnabled && !zoomInhibit) {
zoom.setTarget(rootContext);
zoom.setTargetArea(KDRect(0,0,320,240));
zoom.setViewingArea(KDRect(80*(zoomPosition%3),120-60*(zoomPosition/3),160,120));
rootContext = &zoom;
}
if (gammaEnabled) {
gamma.setTarget(rootContext);
rootContext = &gamma;
}
}
KDIonContext::KDIonContext() :
KDContext(KDPointZero,
KDRect(0, 0, Ion::Display::Width, Ion::Display::Height))
@@ -13,13 +42,22 @@ KDContext(KDPointZero,
}
void KDIonContext::pushRect(KDRect rect, const KDColor * pixels) {
Ion::Display::pushRect(rect, pixels);
if (!rootContext) {
rootContext = &m_realContext;
}
rootContext->pushRect(rect, pixels);
}
void KDIonContext::pushRectUniform(KDRect rect, KDColor color) {
Ion::Display::pushRectUniform(rect, color);
if (!rootContext) {
rootContext = &m_realContext;
}
rootContext->pushRectUniform(rect, color);
}
void KDIonContext::pullRect(KDRect rect, KDColor * pixels) {
Ion::Display::pullRect(rect, pixels);
if (!rootContext) {
rootContext = &m_realContext;
}
rootContext->pullRect(rect, pixels);
}

View File

@@ -0,0 +1,40 @@
#include <kandinsky/postprocess_context.h>
#include <ion.h>
KDPostProcessContext::KDPostProcessContext() :
KDContext(KDPointZero, KDRectZero),
m_target(nullptr)
{
}
void KDPostProcessContext::setOrigin(KDPoint origin) {
KDContext::setOrigin(origin);
assert(m_target);
m_target->setOrigin(origin);
}
void KDPostProcessContext::setClippingRect(KDRect clippingRect) {
KDContext::setClippingRect(clippingRect);
assert(m_target);
m_target->setClippingRect(clippingRect);
}
void KDPostProcessContext::setTarget(KDContext * target)
{
m_target = target;
}
void KDPostProcessContext::pushRect(KDRect rect, const KDColor * pixels)
{
m_target->pushRect(rect, pixels);
}
void KDPostProcessContext::pushRectUniform(KDRect rect, KDColor color)
{
m_target->pushRectUniform(rect, color);
}
void KDPostProcessContext::pullRect(KDRect rect, KDColor * pixels)
{
m_target->pullRect(rect, pixels);
}

View File

@@ -0,0 +1,85 @@
#include <kandinsky/postprocess_gamma_context.h>
#include <ion.h>
#include <math.h>
constexpr int MaxGammaStates = 7;
constexpr float MaxGammaGamut = 0.75;
constexpr float toGamma(int gamma) {
return 1.f / (1 + (float(gamma) / MaxGammaStates * MaxGammaGamut));
}
constexpr int clampGamma(int gamma) {
return gamma < -MaxGammaStates ? -MaxGammaStates : (gamma > MaxGammaStates ? MaxGammaStates : gamma);
}
KDPostProcessGammaContext::KDPostProcessGammaContext() :
m_redGamma(0), m_greenGamma(0), m_blueGamma(0) {}
void KDPostProcessGammaContext::gamma(int& red, int& green, int& blue) {
red = m_redGamma;
green = m_greenGamma;
blue = m_blueGamma;
}
void KDPostProcessGammaContext::gamma(float& red, float& green, float& blue) {
red = (m_redGamma + MaxGammaStates) / float(MaxGammaStates*2);
green = (m_greenGamma + MaxGammaStates) / float(MaxGammaStates*2);
blue = (m_blueGamma + MaxGammaStates) / float(MaxGammaStates*2);
}
void KDPostProcessGammaContext::setGamma(int red, int green, int blue) {
m_redGamma = clampGamma(red);
m_greenGamma = clampGamma(green);
m_blueGamma = clampGamma(blue);
}
void KDPostProcessGammaContext::pushRect(KDRect rect, const KDColor * pixels) {
const float redGamma = toGamma(m_redGamma);
const float greenGamma = toGamma(m_greenGamma);
const float blueGamma = toGamma(m_blueGamma);
KDColor workingBuffer[rect.width()];
for (KDCoordinate y = 0; y < rect.height(); y++) {
KDRect workingRect(rect.x(), rect.y()+y, rect.width(), 1);
for (KDCoordinate x = 0; x < rect.width(); x++) {
const KDColor color = pixels[y*rect.width()+x];
const KDColor result = KDColor::RGB888(
(uint8_t)(powf(color.red()/255.f, redGamma)*255),
(uint8_t)(powf(color.green()/255.f, greenGamma)*255),
(uint8_t)(powf(color.blue()/255.f, blueGamma)*255));
workingBuffer[x] = result;
}
KDPostProcessContext::pushRect(workingRect, workingBuffer);
}
}
void KDPostProcessGammaContext::pushRectUniform(KDRect rect, KDColor color) {
const float redGamma = toGamma(m_redGamma);
const float greenGamma = toGamma(m_greenGamma);
const float blueGamma = toGamma(m_blueGamma);
const KDColor result = KDColor::RGB888(
(uint8_t)(powf(color.red()/255.f, redGamma)*255),
(uint8_t)(powf(color.green()/255.f, greenGamma)*255),
(uint8_t)(powf(color.blue()/255.f, blueGamma)*255));
KDPostProcessContext::pushRectUniform(rect, result);
}
void KDPostProcessGammaContext::pullRect(KDRect rect, KDColor * pixels) {
const float redGamma = 1.f/toGamma(m_redGamma);
const float greenGamma = 1.f/toGamma(m_greenGamma);
const float blueGamma = 1.f/toGamma(m_blueGamma);
KDPostProcessContext::pullRect(rect, pixels);
for (KDCoordinate y = 0; y < rect.height(); y++) {
for (KDCoordinate x = 0; x < rect.width(); x++) {
const KDColor color = pixels[y*rect.width()+x];
const KDColor result = KDColor::RGB888(
(uint8_t)(powf(color.red()/255.f, redGamma)*255),
(uint8_t)(powf(color.green()/255.f,greenGamma)*255),
(uint8_t)(powf(color.blue()/255.f, blueGamma)*255));
pixels[y*rect.width()+x] = result;
}
}
}

View File

@@ -0,0 +1,28 @@
#include <kandinsky/postprocess_invert_context.h>
#include <ion.h>
void KDPostProcessInvertContext::pushRect(KDRect rect, const KDColor * pixels) {
KDColor workingBuffer[rect.width()];
for (KDCoordinate y = 0; y < rect.height(); y++) {
KDRect workingRect(rect.x(), rect.y()+y, rect.width(), 1);
for (KDCoordinate x = 0; x < rect.width(); x++) {
workingBuffer[x] = pixels[y*rect.width()+x].invert();
}
KDPostProcessContext::pushRect(workingRect, workingBuffer);
}
}
void KDPostProcessInvertContext::pushRectUniform(KDRect rect, KDColor color) {
KDPostProcessContext::pushRectUniform(rect, color.invert());
}
void KDPostProcessInvertContext::pullRect(KDRect rect, KDColor * pixels) {
KDPostProcessContext::pullRect(rect, pixels);
for (KDCoordinate y = 0; y < rect.height(); y++) {
for (KDCoordinate x = 0; x < rect.width(); x++) {
pixels[y*rect.width()+x] = pixels[y*rect.width()+x].invert();
}
}
}

View File

@@ -0,0 +1,53 @@
#include <kandinsky/postprocess_zoom_context.h>
#include <ion.h>
KDPostProcessZoomContext::KDPostProcessZoomContext() : m_viewingArea(KDRectZero), m_targetArea(KDRectZero)
{
}
void KDPostProcessZoomContext::pushRect(KDRect rect, const KDColor * pixels) {
auto translatedRect = rect.translatedBy(KDPoint(-m_viewingArea.x(),-m_viewingArea.y()));
auto targetRect = KDRect(translatedRect.x()*2, translatedRect.y()*2, translatedRect.width()*2, translatedRect.height()*2);
auto clippedTargetRect = m_targetArea.intersectedWith(targetRect);
KDColor targetBuffer[targetRect.width()];
for (int y = 0; y < rect.height(); y++) {
for (int x = 0; x < rect.width(); x++) {
targetBuffer[2*x+1] = targetBuffer[2*x] = pixels[y*rect.width()+x];
}
for (int i = 0; i < 2; i++) {
auto outputRect = KDRect(targetRect.x(), targetRect.y()+y*2+i, targetRect.width(), 1);
KDPostProcessContext::pushRect(m_targetArea.intersectedWith(outputRect), targetBuffer+(clippedTargetRect.x()-targetRect.x()));
}
}
}
void KDPostProcessZoomContext::pushRectUniform(KDRect rect, KDColor color) {
auto clippedRect = m_viewingArea.intersectedWith(rect);
auto targetRect = KDRect(clippedRect.x()*2, clippedRect.y()*2, clippedRect.width()*2, clippedRect.height()*2);
targetRect = targetRect.translatedBy(KDPoint(-m_viewingArea.x()*2,-m_viewingArea.y()*2));
targetRect = m_targetArea.intersectedWith(targetRect);
KDPostProcessContext::pushRectUniform(targetRect, color);
}
void KDPostProcessZoomContext::pullRect(KDRect rect, KDColor * pixels) {
auto translatedRect = rect.translatedBy(KDPoint(-m_viewingArea.x(),-m_viewingArea.y()));
auto targetRect = KDRect(translatedRect.x()*2, translatedRect.y()*2, translatedRect.width()*2, translatedRect.height()*2);
auto clippedTargetRect = m_targetArea.intersectedWith(targetRect);
KDColor targetBuffer[targetRect.width()];
for (int y = 0; y < rect.height(); y++) {
memset(targetBuffer, 0x00, sizeof(targetBuffer));
auto outputRect = KDRect(targetRect.x(), targetRect.y()+y*2, targetRect.width(), 1).intersectedWith(m_targetArea);
KDPostProcessContext::pullRect(outputRect, targetBuffer+(clippedTargetRect.x()-targetRect.x()));
for (int x = 0; x < rect.width(); x++) {
pixels[y*rect.width()+x] = targetBuffer[x*2];
}
}
}