diff --git a/imagescrollareagl.cpp b/imagescrollareagl.cpp index 699231f..0a5de09 100644 --- a/imagescrollareagl.cpp +++ b/imagescrollareagl.cpp @@ -14,6 +14,8 @@ #include #include +int FILTERING = 1; + struct RawImageType { QOpenGLTexture::PixelFormat pixelFormat; @@ -375,6 +377,8 @@ void ImageWidget::paintGL() m_program->setUniformValue("bw", m_bwImg && !m_superpixel); m_program->setUniformValue("false_color", m_falseColor && m_bwImg); m_program->setUniformValue("invert", m_invert); + m_program->setUniformValue("filtering", m_scale > 1.0f ? FILTERING : 1); + qDebug() << 1.0 / m_scale; #ifdef COLOR_MANAGMENT m_program->setUniformValue("srgb", m_srgb); #endif diff --git a/settingsdialog.cpp b/settingsdialog.cpp index 770f48e..f43edc7 100644 --- a/settingsdialog.cpp +++ b/settingsdialog.cpp @@ -8,6 +8,7 @@ extern int DEFAULT_WIDTH; extern double SATURATION; +extern int FILTERING; class EvenNumber : public QSpinBox { @@ -59,10 +60,15 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent) m_useNativeDialog = new QCheckBox(tr("Don't use native file dialog"), this); m_useNativeDialog->setChecked(QApplication::testAttribute(Qt::AA_DontUseNativeDialogs)); + m_filtering = new QComboBox(this); + m_filtering->addItems({tr("Nearest"), tr("Bilinear"), tr("Bicubic")}); + m_filtering->setCurrentIndex(FILTERING); + layout->addRow(tr("Image preload count"), m_preloadImages); layout->addRow(tr("Thumbnails size"), m_thumSize); layout->addRow(tr("Saturation"), m_saturation); layout->addRow(m_useNativeDialog); + layout->addRow(tr("Image filtering"), m_filtering); //layout->addRow(new QLabel(tr("Changes in settings will take effect after program restart."))); QDialogButtonBox *buttonBox = new QDialogButtonBox(this); @@ -82,6 +88,7 @@ void SettingsDialog::loadSettings() THUMB_SIZE_BORDER_Y = THUMB_SIZE + 30; DEFAULT_WIDTH = settings.value("settings/preloadimagecount", DEFAULT_WIDTH).toInt(); SATURATION = settings.value("settings/saturation", 95.0).toDouble() / 100.0; + FILTERING = settings.value("settings/filtering", FILTERING).toInt(); QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, settings.value("settings/dontusenativedialogs", false).toBool()); } @@ -102,6 +109,8 @@ void SettingsDialog::saveSettings() settings.setValue("settings/preloadimagecount", m_preloadImages->value()); settings.setValue("settings/dontusenativedialogs", m_useNativeDialog->isChecked()); settings.setValue("settings/saturation", m_saturation->value()); + FILTERING = m_filtering->currentIndex(); + settings.setValue("settings/filtering", FILTERING); SATURATION = m_saturation->value() / 100.0; QApplication::setAttribute(Qt::AA_DontUseNativeDialogs, m_useNativeDialog->isChecked()); if(DEFAULT_WIDTH != m_preloadImages->value()) diff --git a/settingsdialog.h b/settingsdialog.h index f7e90e4..7fb4f9f 100644 --- a/settingsdialog.h +++ b/settingsdialog.h @@ -4,6 +4,7 @@ #include #include #include +#include class SettingsDialog : public QDialog { @@ -21,6 +22,7 @@ private: QSpinBox *m_thumSize; QDoubleSpinBox *m_saturation; QCheckBox *m_useNativeDialog; + QComboBox *m_filtering; }; #endif // SETTINGSDIALOG_H diff --git a/shaders/image.frag b/shaders/image.frag index a1bcc09..c8788fd 100644 --- a/shaders/image.frag +++ b/shaders/image.frag @@ -7,6 +7,7 @@ uniform bool bw; uniform bool invert; uniform bool srgb; uniform bool false_color; +uniform int filtering; in vec2 qt_TexCoord0; layout(location = 0) out vec4 color; @@ -35,7 +36,8 @@ vec3 falsecolor(float color) vec3(1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0));//red color *= 5.0; int i = int(color); - return mix(pallete[i], pallete[i+1], fract(color)); + float f = fract(color); + return mix(pallete[i], pallete[i+1], f);// * (f * 0.5 + 0.5); } vec3 checker() @@ -44,9 +46,104 @@ vec3 checker() return vec3(step(pattern.x * pattern.y, 0.0) * 0.25 + 0.25); } +vec4 cubic(float v) +{ + vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; + vec4 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w) * (1.0/6.0); +} + +vec4 textureBicubic(sampler2D sampler, vec2 texCoords) +{ + vec2 texSize = textureSize(sampler, 0); + vec2 invTexSize = 1.0 / texSize; + + texCoords = texCoords * texSize - 0.5; + + vec2 fxy = fract(texCoords); + texCoords -= fxy; + + vec4 xcubic = cubic(fxy.x); + vec4 ycubic = cubic(fxy.y); + + vec4 c = texCoords.xxyy + vec2 (-0.5, +1.5).xyxy; + + vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); + vec4 offset = c + vec4 (xcubic.yw, ycubic.yw) / s; + + offset *= invTexSize.xxyy; + + vec4 sample0 = texture(sampler, offset.xz); + vec4 sample1 = texture(sampler, offset.yz); + vec4 sample2 = texture(sampler, offset.xw); + vec4 sample3 = texture(sampler, offset.yw); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +} + +vec4 textureCatmul(sampler2D sampler, vec2 texCoords) +{ + ivec2 texSize = textureSize(sampler, 0); + + texCoords = texCoords * vec2(texSize) - 0.5; + + ivec2 texel = ivec2(floor(texCoords)); + vec2 fra = fract(texCoords); + texSize -= 1; + + const mat4 CatMul = mat4(0, 1, 0, 0, -0.5, 0, 0.5, 0, 1, -2.5, 2, -0.5, -0.5, 1.5, -1.5, 0.5); + vec4 xx = CatMul * vec4(1.0, fra.x, fra.x*fra.x, fra.x*fra.x*fra.x); + vec4 yy = CatMul * vec4(1.0, fra.y, fra.y*fra.y, fra.y*fra.y*fra.y); + + vec4 a00 = texelFetch(sampler, clamp(texel + ivec2(-1, -1), ivec2(0, 0), texSize), 0) * xx.x; + vec4 a01 = texelFetch(sampler, clamp(texel + ivec2( 0, -1), ivec2(0, 0), texSize), 0) * xx.y; + vec4 a02 = texelFetch(sampler, clamp(texel + ivec2( 1, -1), ivec2(0, 0), texSize), 0) * xx.z; + vec4 a03 = texelFetch(sampler, clamp(texel + ivec2( 2, -1), ivec2(0, 0), texSize), 0) * xx.w; + vec4 a10 = texelFetch(sampler, clamp(texel + ivec2(-1, 0), ivec2(0, 0), texSize), 0) * xx.x; + vec4 a11 = texelFetch(sampler, clamp(texel + ivec2( 0, 0), ivec2(0, 0), texSize), 0) * xx.y; + vec4 a12 = texelFetch(sampler, clamp(texel + ivec2( 1, 0), ivec2(0, 0), texSize), 0) * xx.z; + vec4 a13 = texelFetch(sampler, clamp(texel + ivec2( 2, 0), ivec2(0, 0), texSize), 0) * xx.w; + vec4 a20 = texelFetch(sampler, clamp(texel + ivec2(-1, 1), ivec2(0, 0), texSize), 0) * xx.x; + vec4 a21 = texelFetch(sampler, clamp(texel + ivec2( 0, 1), ivec2(0, 0), texSize), 0) * xx.y; + vec4 a22 = texelFetch(sampler, clamp(texel + ivec2( 1, 1), ivec2(0, 0), texSize), 0) * xx.z; + vec4 a23 = texelFetch(sampler, clamp(texel + ivec2( 2, 1), ivec2(0, 0), texSize), 0) * xx.w; + vec4 a30 = texelFetch(sampler, clamp(texel + ivec2(-1, 2), ivec2(0, 0), texSize), 0) * xx.x; + vec4 a31 = texelFetch(sampler, clamp(texel + ivec2( 0, 2), ivec2(0, 0), texSize), 0) * xx.y; + vec4 a32 = texelFetch(sampler, clamp(texel + ivec2( 1, 2), ivec2(0, 0), texSize), 0) * xx.z; + vec4 a33 = texelFetch(sampler, clamp(texel + ivec2( 2, 2), ivec2(0, 0), texSize), 0) * xx.w; + + vec4 c = vec4(0.0); + c += (a00 + a01 + a02 + a03) * yy.x; + c += (a10 + a11 + a12 + a13) * yy.y; + c += (a20 + a21 + a22 + a23) * yy.z; + c += (a30 + a31 + a32 + a33) * yy.w; + + return c; +} + void main(void) { - color = texture(qt_Texture0, qt_TexCoord0); + switch(filtering) + { + case 0://nearest + color = texelFetch(qt_Texture0, ivec2(qt_TexCoord0 * textureSize(qt_Texture0, 0)), 0); + break; + default: + case 1://bilinear + color = texture(qt_Texture0, qt_TexCoord0); + break; + case 2://catmul bicubic + color = textureCatmul(qt_Texture0, qt_TexCoord0); + break; + } + color.rgb = color.rgb * unit_scale.x + unit_scale.y; if(bw)color = color.rrra; color = MTF(color, vec4(mtf_param[0], 0.0), vec4(mtf_param[1], 0.5), vec4(mtf_param[2], 1.0));