From a6ec7651fb7140fb8b6c9a2d513954d840df707d Mon Sep 17 00:00:00 2001 From: Vort Date: Mon, 3 Aug 2020 19:31:47 +0300 Subject: [PATCH] TVScreenAnalog with subsample shift shader added --- plugins/channelrx/demodatv/atvdemod.h | 2 +- plugins/channelrx/demodatv/atvdemodbaseband.h | 2 +- plugins/channelrx/demodatv/atvdemodgui.cpp | 2 - plugins/channelrx/demodatv/atvdemodgui.ui | 6 +- plugins/channelrx/demodatv/atvdemodsink.cpp | 2 +- plugins/channelrx/demodatv/atvdemodsink.h | 33 ++- sdrgui/CMakeLists.txt | 2 + sdrgui/gui/glshadertvarray.cpp | 32 +-- sdrgui/gui/glshadertvarray.h | 4 - sdrgui/gui/tvscreen.cpp | 10 +- sdrgui/gui/tvscreen.h | 4 +- sdrgui/gui/tvscreenanalog.cpp | 246 ++++++++++++++++++ sdrgui/gui/tvscreenanalog.h | 88 +++++++ 13 files changed, 373 insertions(+), 60 deletions(-) create mode 100644 sdrgui/gui/tvscreenanalog.cpp create mode 100644 sdrgui/gui/tvscreenanalog.h diff --git a/plugins/channelrx/demodatv/atvdemod.h b/plugins/channelrx/demodatv/atvdemod.h index aae35a695..95133c5f3 100644 --- a/plugins/channelrx/demodatv/atvdemod.h +++ b/plugins/channelrx/demodatv/atvdemod.h @@ -87,7 +87,7 @@ public: } void setScopeSink(BasebandSampleSink* scopeSink) { m_basebandSink->setScopeSink(scopeSink); } - void setTVScreen(TVScreen *tvScreen) { m_basebandSink->setTVScreen(tvScreen); }; //!< set by the GUI + void setTVScreen(TVScreenAnalog *tvScreen) { m_basebandSink->setTVScreen(tvScreen); }; //!< set by the GUI double getMagSq() const { return m_basebandSink->getMagSq(); } //!< Beware this is scaled to 2^30 bool getBFOLocked() { return m_basebandSink->getBFOLocked(); } void setVideoTabIndex(int videoTabIndex) { m_basebandSink->setVideoTabIndex(videoTabIndex); } diff --git a/plugins/channelrx/demodatv/atvdemodbaseband.h b/plugins/channelrx/demodatv/atvdemodbaseband.h index f666bea08..a154a5a83 100644 --- a/plugins/channelrx/demodatv/atvdemodbaseband.h +++ b/plugins/channelrx/demodatv/atvdemodbaseband.h @@ -64,7 +64,7 @@ public: int getChannelSampleRate() const; double getMagSq() const { return m_sink.getMagSq(); } void setScopeSink(BasebandSampleSink* scopeSink) { m_sink.setScopeSink(scopeSink); } - void setTVScreen(TVScreen *tvScreen) { m_sink.setTVScreen(tvScreen); } + void setTVScreen(TVScreenAnalog *tvScreen) { m_sink.setTVScreen(tvScreen); } bool getBFOLocked() { return m_sink.getBFOLocked(); } void setVideoTabIndex(int videoTabIndex) { m_sink.setVideoTabIndex(videoTabIndex); } void setBasebandSampleRate(int sampleRate); //!< To be used when supporting thread is stopped diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index b03c7bb50..02d7f91cc 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -242,8 +242,6 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, Base m_tvSampleRate(48000) { ui->setupUi(this); - ui->screenTV->setColor(false); - ui->screenTV->setExtraColumns(true); setAttribute(Qt::WA_DeleteOnClose, true); connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); diff --git a/plugins/channelrx/demodatv/atvdemodgui.ui b/plugins/channelrx/demodatv/atvdemodgui.ui index 37a18c348..02641c0fd 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.ui +++ b/plugins/channelrx/demodatv/atvdemodgui.ui @@ -1138,7 +1138,7 @@ QLayout::SetMinimumSize - + 0 @@ -1242,9 +1242,9 @@ 1 - TVScreen + TVScreenAnalog QWidget -
gui/tvscreen.h
+
gui/tvscreenanalog.h
1
diff --git a/plugins/channelrx/demodatv/atvdemodsink.cpp b/plugins/channelrx/demodatv/atvdemodsink.cpp index 70ed65d8e..2688640bd 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.cpp +++ b/plugins/channelrx/demodatv/atvdemodsink.cpp @@ -359,7 +359,7 @@ void ATVDemodSink::applyStandard(int sampleRate, const ATVDemodSettings& setting // what is left in a line for the image m_interleaved = true; m_numberOfVSyncLines = 4; - m_numberOfBlackLines = 58; + m_numberOfBlackLines = 59; m_firstVisibleLine = 27; m_numberSamplesHSyncCrop = (int) (0.085f * lineDuration * sampleRate); // 8.5% of full line empirically break; diff --git a/plugins/channelrx/demodatv/atvdemodsink.h b/plugins/channelrx/demodatv/atvdemodsink.h index 6c6a74d58..3469241f5 100644 --- a/plugins/channelrx/demodatv/atvdemodsink.h +++ b/plugins/channelrx/demodatv/atvdemodsink.h @@ -32,7 +32,7 @@ #include "dsp/phasediscri.h" #include "audio/audiofifo.h" #include "util/movingaverage.h" -#include "gui/tvscreen.h" +#include "gui/tvscreenanalog.h" #include "atvdemodsettings.h" @@ -44,7 +44,7 @@ public: virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); void setScopeSink(BasebandSampleSink* scopeSink) { m_scopeSink = scopeSink; } - void setTVScreen(TVScreen *tvScreen) { m_registeredTVScreen = tvScreen; } //!< set by the GUI + void setTVScreen(TVScreenAnalog *tvScreen) { m_registeredTVScreen = tvScreen; } //!< set by the GUI double getMagSq() const { return m_magSqAverage; } //!< Beware this is scaled to 2^30 bool getBFOLocked(); void setVideoTabIndex(int videoTabIndex) { m_videoTabIndex = videoTabIndex; } @@ -107,7 +107,7 @@ private: SampleVector m_scopeSampleBuffer; //*************** ATV PARAMETERS *************** - TVScreen *m_registeredTVScreen; + TVScreenAnalog *m_registeredTVScreen; //int m_intNumberSamplePerLine; int m_numberSamplesPerHTopNom; //!< number of samples per horizontal synchronization pulse (pulse in ultra-black) - nominal value @@ -198,7 +198,7 @@ private: inline void processSample(float& sample, int& sampleVideo) { // Filling pixel on the current line - reference index 0 at start of sync pulse - m_registeredTVScreen->setDataColor(m_sampleIndex - m_numberSamplesPerHSync, sampleVideo, sampleVideo, sampleVideo); + m_registeredTVScreen->setDataColor(m_sampleIndex - m_numberSamplesPerHSync, sampleVideo); if (m_settings.m_hSync) { @@ -286,8 +286,9 @@ private: m_hSyncShiftCount = 0; m_hSyncErrorCount = 0; } - m_registeredTVScreen->renderImage(0, - shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); + m_registeredTVScreen->renderImage(); + //m_registeredTVScreen->renderImage(0, + // shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); } if (m_vSyncDetectSampleCount > m_vSyncDetectThreshold && @@ -319,8 +320,14 @@ private: int rowIndex = m_lineIndex - m_firstVisibleLine; if (m_interleaved) rowIndex = rowIndex * 2 - m_fieldIndex; - m_registeredTVScreen->selectRow(rowIndex); - } + + // TODO: CHANGE + float shiftSamples = 0.0f; + if (m_hSyncShiftCount != 0) + shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; + m_registeredTVScreen->selectRow(rowIndex, + shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); + } // Vertical sync is obtained by skipping horizontal sync on the line that triggers vertical sync (new frame) inline void processEOLHSkip() @@ -342,13 +349,17 @@ private: m_hSyncShiftCount = 0; m_hSyncErrorCount = 0; } - m_registeredTVScreen->renderImage(0, - shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); + m_registeredTVScreen->renderImage(); + //m_registeredTVScreen->renderImage(0, + // shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); m_lineIndex = 0; m_rowIndex = 0; } - m_registeredTVScreen->selectRow(m_rowIndex); + // TODO: CHANGE + float shiftSamples = m_hSyncShiftSum / m_hSyncShiftCount; + m_registeredTVScreen->selectRow(m_rowIndex, + shiftSamples < -1.0f ? -1.0f : (shiftSamples > 1.0f ? 1.0f : shiftSamples)); } }; diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index cde208ddc..3f3507065 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -51,6 +51,7 @@ set(sdrgui_SOURCES gui/transverterbutton.cpp gui/transverterdialog.cpp gui/tvscreen.cpp + gui/tvscreenanalog.cpp gui/valuedial.cpp gui/valuedialz.cpp @@ -123,6 +124,7 @@ set(sdrgui_HEADERS gui/transverterbutton.h gui/transverterdialog.h gui/tvscreen.h + gui/tvscreenanalog.h gui/valuedial.h gui/valuedialz.h diff --git a/sdrgui/gui/glshadertvarray.cpp b/sdrgui/gui/glshadertvarray.cpp index 68a4eb80d..e3375c46b 100644 --- a/sdrgui/gui/glshadertvarray.cpp +++ b/sdrgui/gui/glshadertvarray.cpp @@ -39,13 +39,11 @@ GLShaderTVArray::GLShaderTVArray(bool blnColor) : m_blnColor(blnColor) { m_blnAlphaBlend = false; m_blnAlphaReset = false; - m_blnExtraColumns = false; m_objProgram = 0; m_objImage = 0; m_objTexture = 0; m_intCols = 0; m_intRows = 0; - m_subsampleShift = 0.0f; m_blnInitialized = false; m_objCurrentRow = 0; @@ -112,8 +110,7 @@ void GLShaderTVArray::InitializeGL(int intCols, int intRows) } //Image container - int cols = intCols + (m_blnExtraColumns ? 2 : 0); - m_objImage = new QImage(cols, intRows, QImage::Format_RGBA8888); + m_objImage = new QImage(intCols, intRows, QImage::Format_RGBA8888); m_objImage->fill(QColor(0, 0, 0)); m_objTexture = new QOpenGLTexture(*m_objImage); @@ -158,23 +155,11 @@ void GLShaderTVArray::RenderPixels(unsigned char *chrData) QMatrix4x4 objQMatrix; - float rectHalfWidth = 1.0f; - float sampleSize = 2.0f / m_intCols; - if (m_blnExtraColumns) - rectHalfWidth += sampleSize; - float xShift = sampleSize * m_subsampleShift; - GLfloat arrVertices[] = // 2 3 // 1 4 - { - -rectHalfWidth + xShift, -1.0f, // 1 - -rectHalfWidth + xShift, 1.0f, // 2 - rectHalfWidth + xShift, 1.0f, // 3 - rectHalfWidth + xShift, 1.0f, // 3 - rectHalfWidth + xShift, -1.0f, // 4 - -rectHalfWidth + xShift, -1.0f // 1 - }; + //1 2 3 3 4 1 + { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f }; GLfloat arrTextureCoords[] = // 1 4 @@ -242,9 +227,8 @@ void GLShaderTVArray::RenderPixels(unsigned char *chrData) m_objTexture->bind(); - ptrF->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - m_intCols + (m_blnExtraColumns ? 2 : 0), m_intRows, GL_RGBA, - GL_UNSIGNED_BYTE, m_objImage->bits()); + ptrF->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_intCols, m_intRows, GL_RGBA, + GL_UNSIGNED_BYTE, m_objImage->bits()); ptrF->glEnableVertexAttribArray(0); // vertex ptrF->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, arrVertices); @@ -334,11 +318,9 @@ bool GLShaderTVArray::SetDataColor(int intCol, QRgb objColor) if (m_blnInitialized) { - if ((intCol < m_intCols + m_blnExtraColumns) && - (intCol >= -m_blnExtraColumns) && - (m_objCurrentRow != 0)) + if ((intCol < m_intCols) && (intCol >= 0) && (m_objCurrentRow != 0)) { - m_objCurrentRow[intCol + m_blnExtraColumns] = objColor; + m_objCurrentRow[intCol] = objColor; blnRslt = true; } } diff --git a/sdrgui/gui/glshadertvarray.h b/sdrgui/gui/glshadertvarray.h index 7b9a07600..559a7351f 100644 --- a/sdrgui/gui/glshadertvarray.h +++ b/sdrgui/gui/glshadertvarray.h @@ -43,8 +43,6 @@ public: GLShaderTVArray(bool blnColor); ~GLShaderTVArray(); - void setExtraColumns(bool blnExtraColumns) { m_blnExtraColumns = blnExtraColumns; } - void setSubsampleShift(float subsampleShift) { m_subsampleShift = subsampleShift; } void setColor(bool blnColor) { m_blnColor = blnColor; } void setAlphaBlend(bool blnAlphaBlend) { m_blnAlphaBlend = blnAlphaBlend; } void setAlphaReset() { m_blnAlphaReset = true; } @@ -73,7 +71,6 @@ protected: int m_intCols; int m_intRows; - float m_subsampleShift; QRgb * m_objCurrentRow; @@ -81,7 +78,6 @@ protected: bool m_blnColor; bool m_blnAlphaBlend; bool m_blnAlphaReset; - bool m_blnExtraColumns; }; #endif /* INCLUDE_GUI_GLTVSHADERARRAY_H_ */ diff --git a/sdrgui/gui/tvscreen.cpp b/sdrgui/gui/tvscreen.cpp index e1182999f..26ce7b490 100644 --- a/sdrgui/gui/tvscreen.cpp +++ b/sdrgui/gui/tvscreen.cpp @@ -37,7 +37,6 @@ TVScreen::TVScreen(bool blnColor, QWidget* parent) : m_objTimer.start(40); // capped at 25 FPS m_chrLastData = NULL; - m_subsampleShift = 0.0; m_blnConfigChanged = false; m_blnDataChanged = false; m_blnGLContextInitialized = false; @@ -59,11 +58,6 @@ void TVScreen::setColor(bool blnColor) m_objGLShaderArray.setColor(blnColor); } -void TVScreen::setExtraColumns(bool blnExtraColumns) -{ - m_objGLShaderArray.setExtraColumns(blnExtraColumns); -} - QRgb* TVScreen::getRowBuffer(int intRow) { if (!m_blnGLContextInitialized) @@ -74,10 +68,9 @@ QRgb* TVScreen::getRowBuffer(int intRow) return m_objGLShaderArray.GetRowBuffer(intRow); } -void TVScreen::renderImage(unsigned char * objData, float subsampleShift) +void TVScreen::renderImage(unsigned char * objData) { m_chrLastData = objData; - m_subsampleShift = subsampleShift; m_blnDataChanged = true; } @@ -184,7 +177,6 @@ void TVScreen::paintGL() m_intAskedRows = 0; } - m_objGLShaderArray.setSubsampleShift(m_subsampleShift); m_objGLShaderArray.RenderPixels(m_chrLastData); m_objMutex.unlock(); diff --git a/sdrgui/gui/tvscreen.h b/sdrgui/gui/tvscreen.h index 5ce4fa077..c796ee0ab 100644 --- a/sdrgui/gui/tvscreen.h +++ b/sdrgui/gui/tvscreen.h @@ -46,10 +46,9 @@ public: virtual ~TVScreen(); void setColor(bool blnColor); - void setExtraColumns(bool blnExtraColumns); void resizeTVScreen(int intCols, int intRows); void getSize(int& intCols, int& intRows) const; - void renderImage(unsigned char * objData, float subsampleShift = 0.0); + void renderImage(unsigned char * objData); QRgb* getRowBuffer(int intRow); void resetImage(); void resetImage(int alpha); @@ -74,7 +73,6 @@ private: bool m_blnGLContextInitialized; int m_intAskedCols; int m_intAskedRows; - float m_subsampleShift; // state diff --git a/sdrgui/gui/tvscreenanalog.cpp b/sdrgui/gui/tvscreenanalog.cpp new file mode 100644 index 000000000..c1ba3898a --- /dev/null +++ b/sdrgui/gui/tvscreenanalog.cpp @@ -0,0 +1,246 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Vort // +// Copyright (C) 2018 F4HKW // +// for F4EXB / SDRAngel // +// // +// OpenGL interface modernization. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "tvscreenanalog.h" + +static const char* vertexShaderSource = +"attribute highp vec4 vertex;\n" +"attribute highp vec2 texCoord;\n" +"varying highp vec2 texCoordVar;\n" +"void main() {\n" +" gl_Position = vertex;\n" +" texCoordVar = texCoord;\n" +"}\n"; + +static const char* fragmentShaderSource = +"uniform highp sampler2D uTexture1;\n" +"uniform highp sampler2D uTexture2;\n" +"uniform highp float imw;\n" +"uniform highp float imh;\n" +"uniform highp float tlw;\n" +"uniform highp float tlh;\n" +"varying highp vec2 texCoordVar;\n" +"void main() {\n" +" float tlhw = 0.5 * tlw;" +" float tlhh = 0.5 * tlh;" +" float tys = (texCoordVar.y + tlhh) * imh;\n" +" float p1y = floor(tys) * tlh - tlhh;\n" +" float p3y = p1y + tlh;\n" +" float tshift1 = texture2D(uTexture2, vec2(0.0, p1y)).r;\n" +" float tshift3 = texture2D(uTexture2, vec2(0.0, p3y)).r;\n" +" float shift1 = (1.0 - tshift1 * 2.0) * tlw;\n" +" float shift3 = (1.0 - tshift3 * 2.0) * tlw;\n" +" float txs1 = (texCoordVar.x + shift1 + tlhw) * imw;\n" +" float txs3 = (texCoordVar.x + shift3 + tlhw) * imw;\n" +" float p1x = floor(txs1) * tlw - tlhw;\n" +" float p3x = floor(txs3) * tlw - tlhw;\n" +" float p2x = p1x + tlw;\n" +" float p4x = p3x + tlw;\n" +" float p1 = texture2D(uTexture1, vec2(p1x, p1y)).r;\n" +" float p2 = texture2D(uTexture1, vec2(p2x, p1y)).r;\n" +" float p3 = texture2D(uTexture1, vec2(p3x, p3y)).r;\n" +" float p4 = texture2D(uTexture1, vec2(p4x, p3y)).r;\n" +" float p12 = mix(p1, p2, fract(txs1));\n" +" float p34 = mix(p3, p4, fract(txs3));\n" +" float p = mix(p12, p34, fract(tys));\n" +" gl_FragColor = vec4(p);\n" +"}\n"; + +TVScreenAnalog::TVScreenAnalog(QWidget *parent) + : QGLWidget(parent) +{ + m_objCurrentRow = nullptr; + m_isDataChanged = false; + m_time = 0.0f; + m_cols = 1; + m_rows = 1; + + connect(&m_objTimer, SIGNAL(timeout()), this, SLOT(tick())); + m_objTimer.start(40); // capped at 25 FPS +} + +void TVScreenAnalog::cleanup() +{ + m_shader = nullptr; + m_imageTexture = nullptr; + m_objCurrentRow = nullptr; + m_lineShiftsTexture = nullptr; +} + +void TVScreenAnalog::resizeTVScreen(int intCols, int intRows) +{ + qDebug("TVScreen::resizeTVScreen: cols: %d, rows: %d", intCols, intRows); + m_cols = intCols + 4; + m_rows = intRows; +} + +void TVScreenAnalog::resizeGL(int intWidth, int intHeight) +{ + glViewport(0, 0, intWidth, intHeight); +} + +void TVScreenAnalog::initializeGL() +{ + m_objMutex.lock(); + initializeOpenGLFunctions(); + + m_shader = std::make_shared(this); + m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource); + m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource); + m_shader->link(); + + m_vertexAttribIndex = m_shader->attributeLocation("vertex"); + m_texCoordAttribIndex = m_shader->attributeLocation("texCoord"); + m_objTextureLoc1 = m_shader->uniformLocation("uTexture1"); + m_objTextureLoc2 = m_shader->uniformLocation("uTexture2"); + m_objImageWidthLoc = m_shader->uniformLocation("imw"); + m_objImageHeightLoc = m_shader->uniformLocation("imh"); + m_objTexelWidthLoc = m_shader->uniformLocation("tlw"); + m_objTexelHeightLoc = m_shader->uniformLocation("tlh"); + + + initializeTextures(); + + connect(QOpenGLContext::currentContext(), &QOpenGLContext::aboutToBeDestroyed, + this, &TVScreenAnalog::cleanup); // TODO: when migrating to QOpenGLWidget + + m_objMutex.unlock(); +} + +void TVScreenAnalog::initializeTextures() +{ + m_image = std::make_shared(m_cols, m_rows, QImage::Format_ARGB32); + m_lineShifts = std::make_shared(1, m_rows, QImage::Format_ARGB32); + m_image->fill(0); + m_lineShifts->fill(127); + + m_imageTexture = std::make_shared(*m_image, QOpenGLTexture::DontGenerateMipMaps); + m_lineShiftsTexture = std::make_shared(*m_lineShifts, QOpenGLTexture::DontGenerateMipMaps); + m_imageTexture->setMinificationFilter(QOpenGLTexture::Nearest); + m_imageTexture->setMagnificationFilter(QOpenGLTexture::Nearest); + m_lineShiftsTexture->setMinificationFilter(QOpenGLTexture::Nearest); + m_lineShiftsTexture->setMagnificationFilter(QOpenGLTexture::Nearest); + m_imageTexture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToBorder); + m_imageTexture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge); + m_lineShiftsTexture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); + m_lineShiftsTexture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge); +} + +void TVScreenAnalog::renderImage() +{ + m_isDataChanged = true; +} + +void TVScreenAnalog::tick() +{ + if (m_isDataChanged) { + update(); + } +} + +void TVScreenAnalog::selectRow(int intLine, float shift) +{ + if (!m_image || m_image->height() != m_rows) + return; + + if ((intLine < m_rows) && (intLine >= 0)) + { + m_objCurrentRow = (int*)m_image->scanLine(intLine); + m_lineShifts->setPixel(0, intLine, (1.0f + shift) * 127.5f); + } + else + { + m_objCurrentRow = nullptr; + } +} + +void TVScreenAnalog::setDataColor(int intCol, int objColor) +{ + if ((intCol < m_cols - 2) && + (intCol >= -2) && + (m_objCurrentRow != nullptr)) + { + m_objCurrentRow[intCol + 2] = objColor; + } +} + +void TVScreenAnalog::paintGL() +{ + if (!m_objMutex.tryLock(2)) + return; + + m_isDataChanged = false; + + if (m_image->width() != m_cols || m_image->height() != m_rows) + initializeTextures(); + + float imageWidth = m_image->width(); + float imageHeight = m_image->height(); + float texelWidth = 1.0f / imageWidth; + float texelHeight = 1.0f / imageHeight; + + m_shader->bind(); + m_shader->setUniformValue(m_objTextureLoc1, 0); + m_shader->setUniformValue(m_objTextureLoc2, 1); + m_shader->setUniformValue(m_objImageWidthLoc, imageWidth); + m_shader->setUniformValue(m_objImageHeightLoc, imageHeight); + m_shader->setUniformValue(m_objTexelWidthLoc, texelWidth); + m_shader->setUniformValue(m_objTexelHeightLoc, texelHeight); + + glActiveTexture(GL_TEXTURE0); + m_imageTexture->bind(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + m_image->width(), m_image->height(), GL_RGBA, GL_UNSIGNED_BYTE, m_image->constScanLine(0)); + + glActiveTexture(GL_TEXTURE1); + m_lineShiftsTexture->bind(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + 1, m_image->height(), GL_RGBA, GL_UNSIGNED_BYTE, m_lineShifts->constScanLine(0)); + + float rectHalfWidth = 1.0f + 4 * texelWidth; + GLfloat vertices[] = + { + -rectHalfWidth, -1.0f, + -rectHalfWidth, 1.0f, + rectHalfWidth, 1.0f, + rectHalfWidth, -1.0f + }; + + static const GLfloat arrTextureCoords[] = + { + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f + }; + + glVertexAttribPointer(m_vertexAttribIndex, 2, GL_FLOAT, GL_FALSE, 0, vertices); + glEnableVertexAttribArray(m_vertexAttribIndex); + glVertexAttribPointer(m_texCoordAttribIndex, 2, GL_FLOAT, GL_FALSE, 0, arrTextureCoords); + glEnableVertexAttribArray(m_texCoordAttribIndex); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisableVertexAttribArray(m_vertexAttribIndex); + glDisableVertexAttribArray(m_texCoordAttribIndex); + + m_shader->release(); + + m_objMutex.unlock(); +} \ No newline at end of file diff --git a/sdrgui/gui/tvscreenanalog.h b/sdrgui/gui/tvscreenanalog.h new file mode 100644 index 000000000..70a1cedb8 --- /dev/null +++ b/sdrgui/gui/tvscreenanalog.h @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Vort // +// Copyright (C) 2018 F4HKW // +// for F4EXB / SDRAngel // +// // +// OpenGL interface modernization. // +// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_TVSCREENANALOG_H +#define INCLUDE_TVSCREENANALOG_H + +#include "export.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +class SDRGUI_API TVScreenAnalog : public QGLWidget, protected QOpenGLFunctions +{ + Q_OBJECT + + QTimer m_objTimer; + QMutex m_objMutex; + + bool m_isDataChanged; + + int m_objTextureLoc1; + int m_objTextureLoc2; + int m_objImageWidthLoc; + int m_objImageHeightLoc; + int m_objTexelWidthLoc; + int m_objTexelHeightLoc; + int m_vertexAttribIndex; + int m_texCoordAttribIndex; + std::shared_ptr m_shader; + + float m_time; + + int m_cols; + int m_rows; + + int* m_objCurrentRow; + + std::shared_ptr m_image; + std::shared_ptr m_lineShifts; + std::shared_ptr m_imageTexture; + std::shared_ptr m_lineShiftsTexture; + +public: + TVScreenAnalog(QWidget *parent); + + void resizeTVScreen(int intCols, int intRows); + void selectRow(int intLine, float shift); + void setDataColor(int intCol, int objColor); + void renderImage(); + +private: + void initializeTextures(); + void initializeGL() override; + void paintGL() override; + void resizeGL(int width, int height); + +private slots: + void cleanup(); + void tick(); +}; + +#endif // INCLUDE_TVSCREENANALOG_H