Skip to content

Instantly share code, notes, and snippets.

@sobotka
Created November 26, 2017 23:09
Show Gist options
  • Save sobotka/e77c6bf5bc5e04a53c9cbf09e4aeb417 to your computer and use it in GitHub Desktop.
Save sobotka/e77c6bf5bc5e04a53c9cbf09e4aeb417 to your computer and use it in GitHub Desktop.
Cubic Prefilter Patch against 587b2f105c4e14031f4d70c31d12f9d4da7439ca
diff --git a/source/blender/compositor/intern/COM_SocketReader.h b/source/blender/compositor/intern/COM_SocketReader.h
index 88b018ef8b..0563d9e1b0 100644
--- a/source/blender/compositor/intern/COM_SocketReader.h
+++ b/source/blender/compositor/intern/COM_SocketReader.h
@@ -32,7 +32,8 @@
typedef enum PixelSampler {
COM_PS_NEAREST = 0,
COM_PS_BILINEAR = 1,
- COM_PS_BICUBIC = 2
+ COM_PS_BICUBIC = 2,
+ COM_PS_CUBICBSPLINEPREFILTER = 3
} PixelSampler;
class MemoryBuffer;
diff --git a/source/blender/compositor/operations/COM_ImageOperation.cpp b/source/blender/compositor/operations/COM_ImageOperation.cpp
index fb3efbb67e..459b02afd3 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.cpp
+++ b/source/blender/compositor/operations/COM_ImageOperation.cpp
@@ -40,6 +40,7 @@ BaseImageOperation::BaseImageOperation() : NodeOperation()
{
this->m_image = NULL;
this->m_buffer = NULL;
+ this->m_secondaryBuffer = NULL;
this->m_imageBuffer = NULL;
this->m_imageUser = NULL;
this->m_imagewidth = 0;
@@ -80,6 +81,7 @@ ImBuf *BaseImageOperation::getImBuf()
void BaseImageOperation::initExecution()
{
ImBuf *stackbuf = getImBuf();
+ this->initMutex();
this->m_buffer = stackbuf;
if (stackbuf) {
this->m_imageBuffer = stackbuf->rect_float;
@@ -92,7 +94,12 @@ void BaseImageOperation::initExecution()
void BaseImageOperation::deinitExecution()
{
+ this->deinitMutex();
this->m_imageBuffer = NULL;
+ if (this->m_secondaryBuffer) {
+ IMB_freeImBuf(m_secondaryBuffer);
+ m_secondaryBuffer = NULL;
+ }
}
void BaseImageOperation::determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2])
@@ -108,6 +115,20 @@ void BaseImageOperation::determineResolution(unsigned int resolution[2], unsigne
}
}
+/* Must be located in BaseImageOperation to avoid code duplication */
+void BaseImageOperation::initSecondaryBuffer(int buffer_type)
+{
+ switch (buffer_type) {
+ case BT_DUPLICATE:
+ this->m_secondaryBuffer = IMB_dupImBuf(this->m_buffer);
+ break;
+ case BT_CUBICBSPLINEPREFILTER:
+ this->m_secondaryBuffer = IMB_dupImBuf(this->m_buffer);
+ create_prefilter(this->m_buffer, this->m_secondaryBuffer);
+ break;
+ }
+}
+
void ImageOperation::executePixel(float output[4], float x, float y, PixelSampler sampler)
{
if (this->m_imageBuffer == NULL || x < 0 || y < 0 || x >= this->getWidth() || y >= this->getHeight() ) {
@@ -124,6 +145,13 @@ void ImageOperation::executePixel(float output[4], float x, float y, PixelSample
case COM_PS_BICUBIC:
bicubic_interpolation_color(this->m_buffer, NULL, output, x, y);
break;
+ case COM_PS_CUBICBSPLINEPREFILTER:
+ lockMutex();
+ if (this->m_secondaryBuffer == NULL) {
+ BaseImageOperation::initSecondaryBuffer(BT_CUBICBSPLINEPREFILTER);
+ }
+ unlockMutex();
+ bicubic_interpolation_color(this->m_secondaryBuffer, NULL, output, x, y);
}
}
}
@@ -147,6 +175,13 @@ void ImageAlphaOperation::executePixel(float output[4], float x, float y, PixelS
case COM_PS_BICUBIC:
bicubic_interpolation_color(this->m_buffer, NULL, tempcolor, x, y);
break;
+ case COM_PS_CUBICBSPLINEPREFILTER:
+ lockMutex();
+ if (this->m_secondaryBuffer == NULL) {
+ BaseImageOperation::initSecondaryBuffer(BT_CUBICBSPLINEPREFILTER);
+ }
+ unlockMutex();
+ bicubic_interpolation_color(this->m_secondaryBuffer, NULL, output, x, y);
}
output[0] = tempcolor[3];
}
diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h
index e75e7daa18..9a7d2d4d79 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.h
+++ b/source/blender/compositor/operations/COM_ImageOperation.h
@@ -40,6 +40,7 @@ extern "C" {
class BaseImageOperation : public NodeOperation {
protected:
ImBuf *m_buffer;
+ ImBuf *m_secondaryBuffer;
Image *m_image;
ImageUser *m_imageUser;
float *m_imageBuffer;
@@ -65,14 +66,17 @@ public:
void setImageUser(ImageUser *imageuser) { this->m_imageUser = imageuser; }
void setFramenumber(int framenumber) { this->m_framenumber = framenumber; }
+
+ void initSecondaryBuffer(int buffer_type);
};
class ImageOperation : public BaseImageOperation {
+protected:
public:
/**
* Constructor
*/
ImageOperation();
- void executePixel(float output[4], float x, float y, PixelSampler sampler);
+ void executePixel(float output[4], float x, float y, PixelSampler sampler);
};
class ImageAlphaOperation : public BaseImageOperation {
public:
diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp
index af0d516183..f7450bd560 100644
--- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp
+++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp
@@ -62,6 +62,13 @@ void MultilayerColorOperation::executePixel(float output[4], float x, float y, P
case COM_PS_BICUBIC:
bicubic_interpolation_color(this->m_buffer, NULL, output, x, y);
break;
+ case COM_PS_CUBICBSPLINEPREFILTER:
+ lockMutex();
+ if (this->m_secondaryBuffer == NULL) {
+ BaseImageOperation::initSecondaryBuffer(BT_CUBICBSPLINEPREFILTER);
+ }
+ unlockMutex();
+ bicubic_interpolation_color(this->m_secondaryBuffer, NULL, output, x, y);
}
}
else {
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 28dbe11052..1049477ac1 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -416,6 +416,20 @@ void bilinear_interpolation_color(struct ImBuf *in, unsigned char col[4], float
void bilinear_interpolation_color_wrap(struct ImBuf *in, unsigned char col[4], float col_float[4], float u, float v);
/**
+ * Establish the prefilter algorithm at lowest level to be adopted
+ * by other areas of Blender such as MIPmaps.
+ */
+
+/* Buffer Types */
+#define BT_DUPLICATE 0
+#define BT_CUBICBSPLINEPREFILTER 1
+
+void prefilter_initInitialCausal(float *coeffIn, float *sum, unsigned int length, unsigned int stride);
+void prefilter_initInitialAntiCausal(float *coeffIn);
+void prefilter_doRecursion(float *coeffIn, unsigned int length, unsigned int stride);
+void create_prefilter(struct ImBuf *in, struct ImBuf *out);
+
+/**
*
* \attention defined in readimage.c
*/
diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c
index 863b8424cc..f5662e4fa6 100644
--- a/source/blender/imbuf/intern/imageprocess.c
+++ b/source/blender/imbuf/intern/imageprocess.c
@@ -47,6 +47,7 @@
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
#include <math.h>
+#include <string.h>
/* Only this one is used liberally here, and in imbuf */
void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf)
@@ -461,6 +462,117 @@ void neareast_interpolation(ImBuf *in, ImBuf *out, float x, float y, int xout, i
neareast_interpolation_color(in, outI, outF, x, y);
}
+/*********************** CubicBSpline Prefilter ****************************/
+/* This function creates a prefilter in the frequency domain. It can be
+ viewed as a compliment to a traditional bicubic filter. */
+
+void create_prefilter(ImBuf *in, ImBuf *out)
+{
+ int x, y;
+
+ // A row of float coefficients.
+ float *coeff = out->rect_float;
+
+ if (in == NULL || in->rect_float == NULL || out == NULL ||
+ out->rect_float == NULL || in->x <= 2 || in->y <=2)
+ return;
+
+ // Initialize
+ memcpy(coeff, in->rect_float, in->x * in->y * 4 * sizeof(float));
+
+ // X Recursion
+ for(y = 0; y < in->y; y++)
+ prefilter_doRecursion(&coeff[y * in->x * 4], in->x, 4);
+ // Y Recursion
+ for(x = 0; x < in->x; x++)
+ prefilter_doRecursion(&coeff[x * 4], in->y, in->x * 4);
+
+ // Copy to the out ImBuf.
+ memcpy(out->rect_float, coeff, (in->x * in->y * 4 * sizeof(float)));
+}
+
+// Get the initial causual coefficient for a given run of floats at stride count
+// of floats.
+void prefilter_initInitialCausal(float *coeffIn, float *sum, unsigned int length, unsigned int stride)
+{
+ const float Zp = sqrt(3.0f)-2.0f;
+ const float lambda = (1.0f - Zp) * (1.0f - 1.0f / Zp);
+ unsigned int count = 0, horizon = MIN2(12, length) * 4;
+ float Zn = Zp;
+
+ sum[0] = coeffIn[0]; // R
+ sum[1] = coeffIn[1]; // G
+ sum[2] = coeffIn[2]; // B
+ sum[3] = coeffIn[3]; // A
+
+ for (count = 0; count < horizon; count += stride) {
+ sum[0] += Zn * coeffIn[count]; // R
+ sum[1] += Zn * coeffIn[count+1]; // G
+ sum[2] += Zn * coeffIn[count+2]; // B
+ sum[3] += Zn * coeffIn[count+3]; // A
+
+ Zn *= Zp;
+ }
+
+ coeffIn[0] = (lambda * sum[0]); // R
+ coeffIn[1] = (lambda * sum[1]); // G
+ coeffIn[2] = (lambda * sum[2]); // B
+ coeffIn[3] = (lambda * sum[3]); // A
+}
+
+// Set the initial anti-causual coefficient at the end of the given run of
+// floats at stride count of floats.
+void prefilter_initInitialAntiCausal(float *coeffIn)
+{
+ const float Zp = sqrt(3.0f)-2.0f;
+ const float iZp = (Zp / (Zp - 1.0f));
+
+ coeffIn[0] = iZp * coeffIn[0]; //R
+ coeffIn[1] = iZp * coeffIn[1]; //G
+ coeffIn[2] = iZp * coeffIn[2]; //B
+ coeffIn[3] = iZp * coeffIn[3]; //A
+}
+
+
+void prefilter_doRecursion(float *coeffIn, unsigned int length, unsigned int stride)
+{
+ const float Zp = sqrt(3.0f)-2.0f;
+ const float lambda = (1.0f - Zp) * (1.0f - 1.0f / Zp);
+ float prevcoeff[4] = { 0, 0, 0, 0 };
+ float sum[4] = { 0, 0, 0, 0 };
+ int count = 0;
+ unsigned int total_floats = length * 4;
+
+ prefilter_initInitialCausal(coeffIn, sum, length, 4);
+
+ prevcoeff[0] = coeffIn[0]; // R
+ prevcoeff[1] = coeffIn[1]; // G
+ prevcoeff[2] = coeffIn[2]; // B
+ prevcoeff[3] = coeffIn[3]; // A
+
+ for (count = (1 * stride); count < total_floats; count += stride) {
+ coeffIn[count] = prevcoeff[0] = (lambda * coeffIn[count]) + (Zp * prevcoeff[0]); // R
+ coeffIn[count + 1] = prevcoeff[1] = (lambda * coeffIn[count + 1]) + (Zp * prevcoeff[1]); // G
+ coeffIn[count + 2] = prevcoeff[2] = (lambda * coeffIn[count + 2]) + (Zp * prevcoeff[2]); // B
+ coeffIn[count + 3] = prevcoeff[3] = (lambda * coeffIn[count + 3]) + (Zp * prevcoeff[3]); // A
+ }
+
+ count -= stride;
+ prefilter_initInitialAntiCausal(&coeffIn[count]);
+
+ prevcoeff[0] = coeffIn[count]; // R
+ prevcoeff[1] = coeffIn[count + 1]; // G
+ prevcoeff[2] = coeffIn[count + 2]; // B
+ prevcoeff[3] = coeffIn[count + 3]; // A
+
+ for (count -= stride; count >= 0; count -= stride) {
+ coeffIn[count] = prevcoeff[0] = Zp * (prevcoeff[0] - coeffIn[count]); // R
+ coeffIn[count + 1] = prevcoeff[1] = Zp * (prevcoeff[1] - coeffIn[count + 1]); // G
+ coeffIn[count + 2] = prevcoeff[2] = Zp * (prevcoeff[2] - coeffIn[count + 2]); // B
+ coeffIn[count + 3] = prevcoeff[3] = Zp * (prevcoeff[3] - coeffIn[count + 3]); // A
+ }
+}
+
/*********************** Threaded image processing *************************/
void IMB_processor_apply_threaded(int buffer_lines, int handle_size, void *init_customdata,
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 0d7d20d04a..613526734a 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -137,6 +137,7 @@ EnumPropertyItem node_sampler_type_items[] = {
{0, "NEAREST", 0, "Nearest", ""},
{1, "BILINEAR", 0, "Bilinear", ""},
{2, "BICUBIC", 0, "Bicubic", ""},
+ {3, "CUBICBSPLINEPREFILTER", 0, "Cubic B Spline with Prefilter", ""},
{0, NULL, 0, NULL, NULL}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment