SW rendering when image is too big for texture

This commit is contained in:
2024-11-27 20:21:57 +01:00
parent 4afa940886
commit 9b7837e9fb
2 changed files with 173 additions and 43 deletions
+172 -43
View File
@@ -10,6 +10,7 @@
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QPainter> #include <QPainter>
#include "imageringlist.h" #include "imageringlist.h"
#include <QFloat16>
int FILTERING = 1; int FILTERING = 1;
bool OpenGLES = false; bool OpenGLES = false;
@@ -110,16 +111,15 @@ void ImageWidgetGL::setImage(std::shared_ptr<RawImage> image, int index)
m_error.clear(); m_error.clear();
makeCurrent(); makeCurrent();
m_rawImage = image; m_rawImage = image;
if((int)image->width() > m_maxTextureSize || (int)image->height() > m_maxTextureSize)
{
uint32_t newW = std::min(image->width() * m_maxTextureSize / image->width(), image->width() * m_maxTextureSize / image->height());
uint32_t newH = std::min(image->height() * m_maxTextureSize / image->width(), image->height() * m_maxTextureSize / image->height());
m_rawImage->resize(newW, newH);
}
m_imgWidth = image->width(); m_imgWidth = image->width();
m_imgHeight = image->height(); m_imgHeight = image->height();
bool tooBig = false;
if((int)image->width() > m_maxTextureSize || (int)image->height() > m_maxTextureSize)
{
tooBig = true;
m_swPaint = true;
}
if(!m_image)return; if(!m_image)return;
@@ -133,21 +133,26 @@ void ImageWidgetGL::setImage(std::shared_ptr<RawImage> image, int index)
m_lut->setData(0, 0, 0, LUT_SIZE, LUT_SIZE, LUT_SIZE, 0, QOpenGLTexture::RGBA, QOpenGLTexture::RGBA, QOpenGLTexture::Float16, image->getLUT().data()); m_lut->setData(0, 0, 0, LUT_SIZE, LUT_SIZE, LUT_SIZE, 0, QOpenGLTexture::RGBA, QOpenGLTexture::RGBA, QOpenGLTexture::Float16, image->getLUT().data());
} }
QElapsedTimer timer; if(!tooBig)
timer.start(); {
m_image->destroy(); while(f->glGetError() != GL_NO_ERROR);
m_image->setAutoMipMapGenerationEnabled(false); QElapsedTimer timer;
m_image->setFormat(rawImageType.textureFormat); timer.start();
m_image->setSize(image->width(), image->height()); m_image->destroy();
m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }()); m_image->setAutoMipMapGenerationEnabled(false);
m_image->allocateStorage(); m_image->setFormat(rawImageType.textureFormat);
m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear); m_image->setSize(image->width(), image->height());
m_image->setWrapMode(QOpenGLTexture::ClampToEdge); m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }());
m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, (const void*)image->data()); m_image->allocateStorage();
m_image->bind(); m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); m_image->setWrapMode(QOpenGLTexture::ClampToEdge);
f->glGenerateMipmap(GL_TEXTURE_2D); m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, (const void*)image->data());
qDebug() << "setImage" << timer.elapsed(); m_image->bind();
f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
f->glGenerateMipmap(GL_TEXTURE_2D);
qDebug() << "setImage" << timer.elapsed();
m_swPaint = f->glGetError() != GL_NO_ERROR;
}
m_unit_scale[0] = 1.0f; m_unit_scale[0] = 1.0f;
m_unit_scale[1] = 0.0f; m_unit_scale[1] = 0.0f;
@@ -341,6 +346,123 @@ void ImageWidgetGL::showThumbnail(bool enable)
setOffset(m_dx, m_dy); setOffset(m_dx, m_dy);
} }
void swPaint(std::shared_ptr<RawImage> &rawImage, float dx, float dy, float scale, const MTFParam &mtfParams, QWidget *widget)
{
QPainter painter(widget);
int width = widget->width();
int height = widget->height();
QImage img(width, height, QImage::Format_RGB32);
img.fill(Qt::gray);
int ox = dx;
int oy = dy;
auto mtf = [&mtfParams](int i, float x)
{
x = (x - mtfParams.blackPoint[i]) / (mtfParams.whitePoint[i] - mtfParams.blackPoint[i]);
x = std::min(std::max(x, 0.0f), 1.0f);
return ((mtfParams.midPoint[i] - 1.0f) * x) / ((2.0f * mtfParams.midPoint[i] - 1.0f) * x - mtfParams.midPoint[i]);
};
int imgWidth = rawImage->width();
int imgHeight = rawImage->height();
auto convert = [&](auto *src)
{
float s = 1.0f;
if constexpr(std::numeric_limits<std::remove_reference_t<decltype(*src)>>::is_integer)
s = (float)std::numeric_limits<std::remove_reference_t<decltype(*src)>>::max();
float iscale = 1.0f / scale;
float r[4];
float g[4];
float b[4];
for(int y = std::max(0, -oy); y < height; y++)
{
uint32_t *pixels = (uint32_t*)(img.scanLine(y));
float iptr;
float fy = std::modf((y + oy) * iscale - 0.5f, &iptr);
int py = iptr;
uint32_t w = py * rawImage->widthBytes();
uint32_t w2 = w;
if(py+1 < imgHeight)w2 += rawImage->widthBytes();
if(py >= imgHeight)break;
for(int x = std::max(0, -ox); x < width; x++)
{
float fx = std::modf((x + ox) * iscale - 0.5f, &iptr);
int px = iptr;
int px2 = px + 1 < imgWidth ? px + 1 : px;
if(px >= imgWidth)break;
if(rawImage->channels() > 1)
{
r[0] = src[w + px * 4 + 0];
g[0] = src[w + px * 4 + 1];
b[0] = src[w + px * 4 + 2];
r[1] = src[w + px2 * 4 + 0];
g[1] = src[w + px2 * 4 + 1];
b[1] = src[w + px2 * 4 + 2];
r[2] = src[w2 + px * 4 + 0];
g[2] = src[w2 + px * 4 + 1];
b[2] = src[w2 + px * 4 + 2];
r[3] = src[w2 + px2 * 4 + 0];
g[3] = src[w2 + px2 * 4 + 1];
b[3] = src[w2 + px2 * 4 + 2];
}
else
{
r[0] = src[w + px];
r[2] = src[w2 + px];
r[1] = src[w + px2];
r[3] = src[w2 + px2];
}
uint32_t rgb = 0xff000000;
if(rawImage->channels() > 1)
{
rgb |= (uint8_t)(mtf(0, ((r[3] * fx + r[2] * (1.0f - fx)) * fy + (r[1] * fx + r[0] * (1.0f - fx)) * (1.0f - fy)) / s) * 255.0f) << 16;
rgb |= (uint8_t)(mtf(1, ((g[3] * fx + g[2] * (1.0f - fx)) * fy + (g[1] * fx + g[0] * (1.0f - fx)) * (1.0f - fy)) / s) * 255.0f) << 8;
rgb |= (uint8_t)(mtf(1, ((b[3] * fx + b[2] * (1.0f - fx)) * fy + (b[1] * fx + b[0] * (1.0f - fx)) * (1.0f - fy)) / s) * 255.0f);
}
else
{
uint32_t v = (uint8_t)(mtf(0, ((r[3] * fx + r[2] * (1.0f - fx)) * fy + (r[1] * fx + r[0] * (1.0f - fx)) * (1.0f - fy)) / s) * 255.0f);
rgb = 0xff000000 | (v << 16) | (v << 8) | v;
}
pixels[x] = rgb;
}
}
};
if(rawImage)
{
switch(rawImage->type())
{
case RawImage::UINT8:
convert(static_cast<uint8_t*>(rawImage->data()));
break;
case RawImage::UINT16:
convert(static_cast<uint16_t*>(rawImage->data()));
break;
case RawImage::UINT32:
convert(static_cast<uint32_t*>(rawImage->data()));
break;
case RawImage::FLOAT16:
convert(static_cast<qfloat16*>(rawImage->data()));
break;
case RawImage::FLOAT32:
convert(static_cast<float*>(rawImage->data()));
break;
case RawImage::FLOAT64:
convert(static_cast<double*>(rawImage->data()));
break;
}
}
painter.drawImage(0, 0, img);
}
void ImageWidgetGL::paintGL() void ImageWidgetGL::paintGL()
{ {
float dx = m_dx; float dx = m_dx;
@@ -425,31 +547,38 @@ void ImageWidgetGL::paintGL()
} }
else else
{ {
m_vao->bind(); if(m_swPaint)
debayer();
if(m_superpixel && m_debayerTex)
{ {
f->glActiveTexture(GL_TEXTURE0); swPaint(m_rawImage, dx, dy, m_scale, m_mtfParams, this);
f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
} }
else else
m_image->bind(0); {
m_vao->bind();
debayer();
m_program->bind(); if(m_superpixel && m_debayerTex)
m_program->setUniformValue("viewport", (float)width(), (float)height()); {
m_program->setUniformValue("offset", std::floor(dx), std::floor(dy)); f->glActiveTexture(GL_TEXTURE0);
m_program->setUniformValueArray("mtf_param", m_mtfParams.blackPoint, 3, 3); f->glBindTexture(GL_TEXTURE_2D, m_debayerTex);
m_program->setUniformValue("unit_scale", m_unit_scale[0], m_unit_scale[1]); }
m_program->setUniformValue("zoom", 1.0f/m_scale); else
m_program->setUniformValue("bw", m_bwImg && !m_superpixel); m_image->bind(0);
m_program->setUniformValue("false_color", m_falseColor && m_bwImg);
m_program->setUniformValue("invert", m_invert); m_program->bind();
m_program->setUniformValue("filtering", m_scale > 1.0f ? FILTERING : 1); m_program->setUniformValue("viewport", (float)width(), (float)height());
m_program->setUniformValue("lut_table", 2); m_program->setUniformValue("offset", std::floor(dx), std::floor(dy));
m_program->setUniformValue("srgb", m_srgb); m_program->setUniformValueArray("mtf_param", m_mtfParams.blackPoint, 3, 3);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); m_program->setUniformValue("unit_scale", m_unit_scale[0], m_unit_scale[1]);
m_vao->release(); m_program->setUniformValue("zoom", 1.0f/m_scale);
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);
m_program->setUniformValue("lut_table", 2);
m_program->setUniformValue("srgb", m_srgb);
f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_vao->release();
}
} }
} }
+1
View File
@@ -91,6 +91,7 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget
Database *m_database = nullptr; Database *m_database = nullptr;
QPointF m_lastPos; QPointF m_lastPos;
QString m_error; QString m_error;
bool m_swPaint = false;
public: public:
explicit ImageWidgetGL(Database *database, QWidget *parent = nullptr); explicit ImageWidgetGL(Database *database, QWidget *parent = nullptr);
~ImageWidgetGL() override; ~ImageWidgetGL() override;